| //@ requireOptions("--useBBQJIT=1") |
| //@ skip |
| // Failure: |
| // Exception: CompileError: WebAssembly.Module doesn't parse at byte 12: 0th type failed to parse because struct types are not enabled (evaluating 'new WebAssembly.Module(this.toBuffer(debug))') |
| // Module@[native code] |
| // toModule@.tests/wasm.yaml/wasm/v8/wasm-module-builder.js:2082:34 |
| // instantiate@.tests/wasm.yaml/wasm/v8/wasm-module-builder.js:2071:31 |
| // ArrayCopyBenchmark@array-copy-benchmark.js:99:37 |
| // global code@array-copy-benchmark.js:115:3 |
| |
| // Copyright 2021 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Flags: --experimental-wasm-gc --no-liftoff --experimental-wasm-nn-locals |
| |
| load("wasm-module-builder.js"); |
| |
| // This is benchmark to investigate at which point it is more efficient to call |
| // a memcpy-based builtin for array.copy, rather than copying |
| // element-by-element. |
| // How to run: |
| // - Set {iterations} to a high number to get better measurements |
| // - Change the value of {length} to find point at which the builtin becomes |
| // faster. |
| // - Change {array_type} if you want to test different types. |
| // Right now, the limit is found to be around 10. |
| (function ArrayCopyBenchmark() { |
| |
| let array_length = 10; |
| let iterations = 1; |
| |
| var builder = new WasmModuleBuilder(); |
| let struct_index = builder.addStruct([makeField(kWasmI32, true), |
| makeField(kWasmI8, false)]); |
| let array_type = kWasmI32; // Also try kWasmI64, wasmRefNullType(struct_index) |
| var array_index = builder.addArray(array_type, true); |
| var from = builder.addGlobal(wasmRefNullType(array_index), true); |
| var to = builder.addGlobal(wasmRefNullType(array_index), true); |
| |
| builder.addFunction("init", kSig_v_v) |
| .addBody([ |
| ...wasmI32Const(array_length), |
| kGCPrefix, kExprArrayNewDefault, array_index, |
| kExprGlobalSet, from.index, |
| ...wasmI32Const(array_length), |
| kGCPrefix, kExprArrayNewDefault, array_index, |
| kExprGlobalSet, to.index |
| ]) |
| .exportFunc(); |
| |
| builder.addFunction("array_copy", kSig_v_v) |
| .addLocals(kWasmI32, 1) |
| .addBody([ |
| kExprLoop, kWasmVoid, |
| kExprGlobalGet, to.index, |
| ...wasmI32Const(0), |
| kExprGlobalGet, from.index, |
| ...wasmI32Const(0), |
| ...wasmI32Const(array_length), |
| kGCPrefix, kExprArrayCopy, array_index, array_index, |
| // Outer loop: run everything {iterations} times. |
| kExprLocalGet, 0, kExprI32Const, 1, kExprI32Add, kExprLocalSet, 0, |
| kExprLocalGet, 0, ...wasmI32Const(iterations), kExprI32LtS, |
| kExprBrIf, 0, |
| kExprEnd]) |
| .exportFunc(); |
| |
| builder.addFunction("loop_copy", kSig_v_v) |
| .addLocals(wasmRefType(array_index), 2) |
| .addLocals(kWasmI32, 2) |
| .addBody([ |
| kExprLoop, kWasmVoid, |
| ...wasmI32Const(0), |
| kExprLocalSet, 2, |
| kExprGlobalGet, from.index, kExprRefAsNonNull, kExprLocalSet, 0, |
| kExprGlobalGet, to.index, kExprRefAsNonNull, kExprLocalSet, 1, |
| kExprLoop, kWasmVoid, |
| kExprLocalGet, 1, // array |
| kExprLocalGet, 2, // index |
| // value |
| kExprLocalGet, 0, kExprLocalGet, 2, |
| kGCPrefix, kExprArrayGet, array_index, |
| // array.set |
| kGCPrefix, kExprArraySet, array_index, |
| // index++ |
| kExprLocalGet, 2, kExprI32Const, 1, kExprI32Add, kExprLocalSet, 2, |
| // if (index < array_length) goto loop; |
| kExprLocalGet, 2, ...wasmI32Const(array_length), kExprI32LtU, |
| kExprBrIf, 0, |
| kExprEnd, |
| // Outer loop: run everything {iterations} times. |
| kExprLocalGet, 3, kExprI32Const, 3, kExprI32Add, kExprLocalSet, 3, |
| kExprLocalGet, 3, ...wasmI32Const(iterations), kExprI32LtS, |
| kExprBrIf, 0, |
| kExprEnd]) |
| .exportFunc(); |
| |
| var instance = builder.instantiate({}); |
| |
| instance.exports.init(); |
| // print("Array length: " + array_length + ", #iterations: " + iterations); |
| { |
| let before = Date.now(); |
| instance.exports.array_copy(); |
| let after = Date.now(); |
| // print("array.copy: " + (after - before) + "ms"); |
| } |
| { |
| let before = Date.now(); |
| instance.exports.loop_copy(); |
| let after = Date.now(); |
| // print("loop copy: " + (after - before) + "ms"); |
| } |
| })(); |
| |
| // The following two benchmarks measure the efficiency of array allocation. |
| // Change the three first parameters of each benchmarks to measure different |
| // array sizes, iterations, and element types. |
| (function ArrayNewDefaultBenchmark() { |
| let array_length = 10; |
| let iterations = 1 |
| let test_object_type = false; |
| |
| var builder = new WasmModuleBuilder(); |
| let struct_index = builder.addStruct([makeField(kWasmI32, true), |
| makeField(kWasmI8, true)]); |
| let array_type = test_object_type ? wasmRefNullType(struct_index) : kWasmI32; |
| var array_index = builder.addArray(array_type, true); |
| |
| let array_new = builder.addFunction( |
| "array_new", makeSig([], [wasmRefNullType(array_index)])) |
| .addBody([ |
| ...wasmI32Const(array_length), |
| kGCPrefix, kExprArrayNewDefault, array_index]) |
| .exportFunc(); |
| |
| builder.addFunction("loop_array_new", kSig_v_v) |
| .addLocals(wasmRefNullType(array_index), 1) |
| .addLocals(kWasmI32, 1) |
| .addBody([ |
| kExprLoop, kWasmVoid, |
| kExprCallFunction, array_new.index, |
| kExprLocalSet, 0, |
| kExprLocalGet, 1, kExprI32Const, 1, kExprI32Add, kExprLocalTee, 1, |
| ...wasmI32Const(iterations), kExprI32LtS, |
| kExprBrIf, 0, |
| kExprEnd]) |
| .exportFunc(); |
| |
| var instance = builder.instantiate({}); |
| |
| /* |
| print(`Type: ${test_object_type ? "object" : "i32"}, array length: ` + |
| `${array_length}, #iterations: ${iterations}`); |
| */ |
| let before = Date.now(); |
| instance.exports.loop_array_new(); |
| let after = Date.now(); |
| // print(`array.new_default: ${after - before} ms`); |
| })(); |
| |
| (function ArrayNewBenchmark() { |
| let array_length = 10; |
| let iterations = 1; |
| let test_object_type = true; |
| |
| var builder = new WasmModuleBuilder(); |
| let struct_index = builder.addStruct([makeField(kWasmI32, true), |
| makeField(kWasmI8, true)]); |
| let array_type = test_object_type ? wasmRefNullType(struct_index) : kWasmI32; |
| var array_index = builder.addArray(array_type, true); |
| |
| let array_new = builder.addFunction( |
| "array_new", makeSig([], [wasmRefNullType(array_index)])) |
| .addBody([ |
| ...(test_object_type ? [kGCPrefix, kExprStructNewDefault, struct_index] |
| : wasmI32Const(10)), |
| ...wasmI32Const(array_length), |
| kGCPrefix, kExprArrayNew, array_index]) |
| .exportFunc(); |
| |
| builder.addFunction("loop_array_new", kSig_v_v) |
| .addLocals(wasmRefNullType(array_index), 1) |
| .addLocals(kWasmI32, 1) |
| .addBody([ |
| kExprLoop, kWasmVoid, |
| kExprCallFunction, array_new.index, |
| kExprLocalSet, 0, |
| kExprLocalGet, 1, kExprI32Const, 1, kExprI32Add, kExprLocalTee, 1, |
| ...wasmI32Const(iterations), kExprI32LtS, |
| kExprBrIf, 0, |
| kExprEnd]) |
| .exportFunc(); |
| |
| var instance = builder.instantiate({}); |
| |
| /* |
| print(`Type: ${test_object_type ? "object" : "i32"}, array length: ` + |
| `${array_length}, #iterations: ${iterations}`); |
| */ |
| let before = Date.now(); |
| instance.exports.loop_array_new(); |
| let after = Date.now(); |
| // print(`array.new: ${after - before} ms`); |
| })(); |