Skip to content

Instantly share code, notes, and snippets.

@binji
Created October 26, 2017 22:18
Show Gist options
  • Save binji/b8e8bc0c0121235d9f1668bc447c7f8c to your computer and use it in GitHub Desktop.
Save binji/b8e8bc0c0121235d9f1668bc447c7f8c to your computer and use it in GitHub Desktop.
Bulk Memory benchmark
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