Created
October 26, 2017 22:18
-
-
Save binji/b8e8bc0c0121235d9f1668bc447c7f8c to your computer and use it in GitHub Desktop.
Bulk Memory benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const page_size = 65536; | |
const memory_pages = 2048 * 2; | |
let memory = new WebAssembly.Memory({initial: memory_pages}); | |
let buffer = new Uint8Array(memory.buffer); | |
let registry = {env: {memory}}; | |
function module(bytes) { | |
let buffer = new ArrayBuffer(bytes.length); | |
let view = new Uint8Array(buffer); | |
for (let i = 0; i < bytes.length; ++i) { | |
view[i] = bytes.charCodeAt(i); | |
} | |
return new WebAssembly.Module(buffer); | |
} | |
function instance(bytes, imports = registry) { | |
return new WebAssembly.Instance(module(bytes), imports); | |
} | |
function human_size(bytes) { | |
if (bytes >= 1024 * 1024) { | |
return (bytes / (1024 * 1024)).toFixed(1) + 'Mib'; | |
} else if (bytes >= 1024) { | |
return (bytes / 1024).toFixed(1) + 'Kib'; | |
} else { | |
return bytes + 'b'; | |
} | |
} | |
function print_range(from, to) { | |
for (let i = from; i < to; ++i) { | |
print('[' + i + '] = ' + buffer[i]); | |
} | |
} | |
let builtin_memcpy = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x08\x01\x60\x03\x7f\x7f\x7f\x01\x7f\x02\x0f\x01\x03\x65\x6e\x76\x06\x6d\x65\x6d\x6f\x72\x79\x02\x00\x01\x03\x03\x02\x00\x00\x07\x16\x02\x07\x5f\x6d\x65\x6d\x63\x70\x79\x00\x00\x08\x5f\x6d\x65\x6d\x6d\x6f\x76\x65\x00\x01\x0a\x0b\x02\x04\x00\x41\x00\x0b\x04\x00\x41\x00\x0b"); | |
let wasm_memcpy1 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x08\x01\x60\x03\x7f\x7f\x7f\x01\x7f\x02\x0f\x01\x03\x65\x6e\x76\x06\x6d\x65\x6d\x6f\x72\x79\x02\x00\x01\x03\x03\x02\x00\x00\x07\x1a\x02\x09\x6d\x79\x5f\x6d\x65\x6d\x63\x70\x79\x00\x00\x0a\x6d\x79\x5f\x6d\x65\x6d\x6d\x6f\x76\x65\x00\x01\x0a\x8e\x02\x02\xa8\x01\x01\x01\x7f\x02\x7f\x20\x00\x21\x03\x20\x00\x41\x03\x71\x20\x01\x41\x03\x71\x46\x04\x40\x03\x40\x20\x00\x41\x03\x71\x04\x40\x20\x02\x45\x04\x40\x20\x03\x0f\x0b\x20\x00\x20\x01\x2c\x00\x00\x3a\x00\x00\x20\x00\x41\x01\x6a\x21\x00\x20\x01\x41\x01\x6a\x21\x01\x20\x02\x41\x01\x6b\x21\x02\x0c\x01\x0b\x0b\x03\x40\x20\x02\x41\x04\x4e\x04\x40\x20\x00\x20\x01\x28\x02\x00\x36\x02\x00\x20\x00\x41\x04\x6a\x21\x00\x20\x01\x41\x04\x6a\x21\x01\x20\x02\x41\x04\x6b\x21\x02\x0c\x01\x0b\x0b\x0b\x03\x40\x20\x02\x41\x00\x4a\x04\x40\x20\x00\x20\x01\x2c\x00\x00\x3a\x00\x00\x20\x00\x41\x01\x6a\x21\x00\x20\x01\x41\x01\x6a\x21\x01\x20\x02\x41\x01\x6b\x21\x02\x0c\x01\x0b\x0b\x20\x03\x0b\x0b\x62\x01\x01\x7f\x02\x7f\x20\x01\x20\x00\x48\x20\x00\x20\x01\x20\x02\x6a\x48\x71\x04\x40\x20\x00\x21\x03\x20\x01\x20\x02\x6a\x21\x01\x20\x00\x20\x02\x6a\x21\x00\x03\x40\x20\x02\x41\x00\x4a\x04\x40\x20\x02\x41\x01\x6b\x21\x02\x20\x00\x41\x01\x6b\x22\x00\x20\x01\x41\x01\x6b\x22\x01\x2c\x00\x00\x3a\x00\x00\x0c\x01\x0b\x0b\x20\x03\x21\x00\x05\x20\x00\x20\x01\x20\x02\x10\x00\x1a\x0b\x20\x00\x0b\x0b"); | |
// Unroll to 8 bytes | |
let wasm_memcpy2 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x08\x01\x60\x03\x7f\x7f\x7f\x01\x7f\x02\x0f\x01\x03\x65\x6e\x76\x06\x6d\x65\x6d\x6f\x72\x79\x02\x00\x01\x03\x02\x01\x00\x07\x0d\x01\x09\x6d\x79\x5f\x6d\x65\x6d\x63\x70\x79\x00\x00\x0a\xb5\x01\x01\xb2\x01\x01\x01\x7f\x02\x7f\x20\x00\x21\x03\x20\x00\x41\x03\x71\x20\x01\x41\x03\x71\x46\x04\x40\x03\x40\x20\x00\x41\x03\x71\x04\x40\x20\x02\x45\x04\x40\x20\x03\x0f\x0b\x20\x00\x20\x01\x2c\x00\x00\x3a\x00\x00\x20\x00\x41\x01\x6a\x21\x00\x20\x01\x41\x01\x6a\x21\x01\x20\x02\x41\x01\x6b\x21\x02\x0c\x01\x0b\x0b\x03\x40\x20\x02\x41\x08\x4e\x04\x40\x20\x00\x20\x01\x28\x02\x00\x36\x02\x00\x20\x00\x20\x01\x28\x02\x04\x36\x02\x04\x20\x00\x41\x08\x6a\x21\x00\x20\x01\x41\x08\x6a\x21\x01\x20\x02\x41\x08\x6b\x21\x02\x0c\x01\x0b\x0b\x0b\x03\x40\x20\x02\x41\x00\x4a\x04\x40\x20\x00\x20\x01\x2c\x00\x00\x3a\x00\x00\x20\x00\x41\x01\x6a\x21\x00\x20\x01\x41\x01\x6a\x21\x01\x20\x02\x41\x01\x6b\x21\x02\x0c\x01\x0b\x0b\x20\x03\x0b\x0b"); | |
// Unroll to 16 bytes (64 bit) | |
let wasm_memcpy3 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x08\x01\x60\x03\x7f\x7f\x7f\x01\x7f\x02\x0f\x01\x03\x65\x6e\x76\x06\x6d\x65\x6d\x6f\x72\x79\x02\x00\x01\x03\x02\x01\x00\x07\x0d\x01\x09\x6d\x79\x5f\x6d\x65\x6d\x63\x70\x79\x00\x00\x0a\xb5\x01\x01\xb2\x01\x01\x01\x7f\x02\x7f\x20\x00\x21\x03\x20\x00\x41\x03\x71\x20\x01\x41\x03\x71\x46\x04\x40\x03\x40\x20\x00\x41\x03\x71\x04\x40\x20\x02\x45\x04\x40\x20\x03\x0f\x0b\x20\x00\x20\x01\x2c\x00\x00\x3a\x00\x00\x20\x00\x41\x01\x6a\x21\x00\x20\x01\x41\x01\x6a\x21\x01\x20\x02\x41\x01\x6b\x21\x02\x0c\x01\x0b\x0b\x03\x40\x20\x02\x41\x10\x4e\x04\x40\x20\x00\x20\x01\x29\x03\x00\x37\x03\x00\x20\x00\x20\x01\x29\x03\x08\x37\x03\x08\x20\x00\x41\x10\x6a\x21\x00\x20\x01\x41\x10\x6a\x21\x01\x20\x02\x41\x10\x6b\x21\x02\x0c\x01\x0b\x0b\x0b\x03\x40\x20\x02\x41\x00\x4a\x04\x40\x20\x00\x20\x01\x2c\x00\x00\x3a\x00\x00\x20\x00\x41\x01\x6a\x21\x00\x20\x01\x41\x01\x6a\x21\x01\x20\x02\x41\x01\x6b\x21\x02\x0c\x01\x0b\x0b\x20\x03\x0b\x0b"); | |
// Unroll to 32 bytes (64 bit) | |
let wasm_memcpy4 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x08\x01\x60\x03\x7f\x7f\x7f\x01\x7f\x02\x0f\x01\x03\x65\x6e\x76\x06\x6d\x65\x6d\x6f\x72\x79\x02\x00\x01\x03\x02\x01\x00\x07\x0d\x01\x09\x6d\x79\x5f\x6d\x65\x6d\x63\x70\x79\x00\x00\x0a\xc9\x01\x01\xc6\x01\x01\x01\x7f\x02\x7f\x20\x00\x21\x03\x20\x00\x41\x03\x71\x20\x01\x41\x03\x71\x46\x04\x40\x03\x40\x20\x00\x41\x03\x71\x04\x40\x20\x02\x45\x04\x40\x20\x03\x0f\x0b\x20\x00\x20\x01\x2c\x00\x00\x3a\x00\x00\x20\x00\x41\x01\x6a\x21\x00\x20\x01\x41\x01\x6a\x21\x01\x20\x02\x41\x01\x6b\x21\x02\x0c\x01\x0b\x0b\x03\x40\x20\x02\x41\x20\x4e\x04\x40\x20\x00\x20\x01\x29\x03\x00\x37\x03\x00\x20\x00\x20\x01\x29\x03\x08\x37\x03\x08\x20\x00\x20\x01\x29\x03\x10\x37\x03\x10\x20\x00\x20\x01\x29\x03\x18\x37\x03\x18\x20\x00\x41\x20\x6a\x21\x00\x20\x01\x41\x20\x6a\x21\x01\x20\x02\x41\x20\x6b\x21\x02\x0c\x01\x0b\x0b\x0b\x03\x40\x20\x02\x41\x00\x4a\x04\x40\x20\x00\x20\x01\x2c\x00\x00\x3a\x00\x00\x20\x00\x41\x01\x6a\x21\x00\x20\x01\x41\x01\x6a\x21\x01\x20\x02\x41\x01\x6b\x21\x02\x0c\x01\x0b\x0b\x20\x03\x0b\x0b"); | |
const Kib = 1024; | |
const Mib = 1024 * 1024; | |
const Gib = 1024 * 1024 * 1024; | |
const memory_size = memory_pages * page_size; | |
let tests = [ | |
{size: 32}, | |
{size: 64}, | |
{size: 128}, | |
{size: 256}, | |
{size: 512}, | |
{size: 1024}, | |
{size: 2 * Kib}, | |
{size: 4 * Kib}, | |
{size: 8 * Kib}, | |
{size: 16 * Kib}, | |
{size: 32 * Kib}, | |
{size: 64 * Kib}, | |
{size: 128 * Kib}, | |
{size: 256 * Kib}, | |
{size: 512 * Kib}, | |
{size: 1024 * Kib}, | |
]; | |
function measure(size, f) { | |
let src_base = 0; | |
let dst_base = memory_size / 2; | |
let times = Gib / size; | |
let mask = 1 * Mib - 1; | |
// warm-up. | |
for (let i = 0; i < times / 10; ++i) { | |
f(dst_base, src_base, size); | |
} | |
let total_time = 0; | |
let total_copied = 0; | |
/* | |
// initialize data. | |
for (let i = 0; i < mask + 1; ++i) { | |
buffer[src_base + i] = i & 0xff; | |
buffer[dst_base + i] = 0; | |
} | |
*/ | |
let src = 0; | |
let dst = 0; | |
let start = performance.now(); | |
for (let i = 0; i < times; ++i) { | |
f(dst_base + dst, src_base + src, size); | |
dst = ((dst + size) & mask); | |
src = ((src + size) & mask); | |
} | |
let end = performance.now(); | |
/* | |
src = 0; | |
dst = 0; | |
let start2 = performance.now(); | |
for (let i = 0; i < times; ++i) { | |
f(dst_base + dst, src_base + src, size); | |
f(dst_base + dst, src_base + src, size); | |
dst = ((dst + size) & mask); | |
src = ((src + size) & mask); | |
} | |
let end2 = performance.now(); | |
*/ | |
// total_time = (end2 - start2) - (end - start); | |
total_time = (end - start); | |
total_copied = Gib; | |
/* | |
for (let i = 0; i < mask + 1; ++i) { | |
if (buffer[dst_base + i] != (i & 0xff)) { | |
throw new Error( | |
'Value mismatch at ' + i + ' ! Got ' + buffer[dst_base + i] + | |
', expected ' + (i & 0xff)); | |
} | |
} | |
*/ | |
let elapsed = total_time; | |
let gb_per_sec = (total_copied * 1000) / (total_time * Gib); | |
return {elapsed, gb_per_sec} | |
} | |
function table_cell(m) { | |
// return m.gb_per_sec.toFixed(3) + ' Gib/s in ' + m.elapsed.toFixed(3) + 'ms | '; | |
return m.gb_per_sec.toFixed(3) + ' Gib/s | '; | |
} | |
let columns = [ | |
{name: 'intrinsic', func: builtin_memcpy.exports._memcpy}, | |
{name: 'i64 load/store x 4', func: wasm_memcpy4.exports.my_memcpy}, | |
{name: 'i64 load/store x 2', func: wasm_memcpy3.exports.my_memcpy}, | |
{name: 'i32 load/store x 2', func: wasm_memcpy2.exports.my_memcpy}, | |
{name: 'i32 load/store', func: wasm_memcpy1.exports.my_memcpy}, | |
]; | |
if (true) { | |
let header = '| |'; | |
for (let column of columns) { header += ' ' + column.name + ' |'; } | |
print(header); | |
header = '| --- |'; | |
for (let column of columns) { header += ' --- |'; } | |
print(header); | |
for (let test of tests) { | |
let size = test.size; | |
let times = Gib / test.size; | |
let message = '| size=' + human_size(size) + ', N=' + times + ' | '; | |
for (let column of columns) { | |
message += table_cell(measure(size, column.func)); | |
} | |
print(message); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment