From 7359949dd6c15ee1a318bdb0765b9246a13ee928 Mon Sep 17 00:00:00 2001 From: Su Yihan Date: Tue, 5 Mar 2024 09:41:27 +0800 Subject: [PATCH] enable running generated wasm module in chrome (#148) * enable running generated wasm module in chrome --------- Signed-off-by: Su Yihan --- tests/benchmark/README.md | 4 +- tests/benchmark/run_benchmark.js | 88 +++++++++++++++---- .../import_object.js | 6 +- .../readme.md} | 23 +++-- .../run_module/run_module_on_chrome.html | 62 +++++++++++++ .../run_module/run_module_on_chrome.js | 68 ++++++++++++++ .../run_module_on_node.js} | 0 7 files changed, 224 insertions(+), 27 deletions(-) rename tools/validate/{run_module_on_node => run_module}/import_object.js (98%) rename tools/validate/{run_module_on_node/run_module_on_node.md => run_module/readme.md} (73%) create mode 100644 tools/validate/run_module/run_module_on_chrome.html create mode 100644 tools/validate/run_module/run_module_on_chrome.js rename tools/validate/{run_module_on_node/run_module.js => run_module/run_module_on_node.js} (100%) diff --git a/tests/benchmark/README.md b/tests/benchmark/README.md index 57fc291b..2afc5dbf 100644 --- a/tests/benchmark/README.md +++ b/tests/benchmark/README.md @@ -39,7 +39,9 @@ These benchmarks are based on some open source efforts to measure performance of # run specific benchmark node run_benchmark.js --benchmark binarytrees # run specific runtime mode - node run_benchmark.js --runtime wamr-aot # (wamr-aot | wamr-interp | qjs) + node run_benchmark.js --runtimes wamr-aot # (wamr-aot | wamr-interp | qjs | node) + # get result after multiple times warm up + node run_benchmark.js --warmup 3 ``` ## Validate benchmark result diff --git a/tests/benchmark/run_benchmark.js b/tests/benchmark/run_benchmark.js index bb23c41c..72bdc624 100644 --- a/tests/benchmark/run_benchmark.js +++ b/tests/benchmark/run_benchmark.js @@ -54,6 +54,7 @@ const wamr_stack_size = args['--stack-size'] ? parseInt(args['--stack-size']) : const wamr_gc_heap = args['--gc-heap'] ? parseInt(args['--gc-heap']) : 40960000; const specifed_benchmarks = args['--benchmarks'] ? args['--benchmarks'].split(',') : null; const specified_runtimes = args['--runtimes'] ? args['--runtimes'].split(',') : null; +const warm_up_times = args['--warmup'] ? parseInt(args['--warmup']) : 0; const default_gc_size_option = `--gc-heap-size=${wamr_gc_heap}` const stack_size_option = `--stack-size=${wamr_stack_size}` @@ -80,9 +81,27 @@ try { } } -let ts_times = []; -let js_times = []; -let aot_times = []; +let node_cmd; +try { + node_cmd = execSync('which node').toString().trim(); +} catch (error) { + if (process.env.NODE_PATH) { + node_cmd = process.env.NODE_PATH; + } else { + const default_node_path = '/usr/local/bin/node'; + if (fs.existsSync(default_node_path)) { + node_cmd = default_node_path; + } else { + console.error("Error: NODE_PATH is not defined, and no default node path is provided."); + process.exit(1); + } + } +} + +let wamr_interp_times = []; +let qjs_js_times = []; +let wamr_aot_times = []; +let v8_js_times = []; let prefixs = []; let benchmark_options = { @@ -118,6 +137,7 @@ function collect_benchmark_options(options) { console.log(`\x1b[33m======================== options ========================\x1b[0m`); console.log(`QJS_PATH: ${qjs}`); +console.log(`NODE_PATH: ${node_cmd}`); console.log(`strategy: run ${multirun} times and get average`); console.log(`clean generated files: ${shouldClean ? 'true' : 'false'}`); console.log(`\x1b[33m======================== running ========================\x1b[0m`); @@ -127,6 +147,9 @@ function run_multiple_times(cmd) { let elapse_arr = []; try { + for (let i = 0; i < warm_up_times; i++) { + execSync(cmd); + } for (let i = 0; i < multirun; i++) { let start = performance.now(); let ret = execSync(cmd); @@ -192,7 +215,7 @@ for (let benchmark of benchmarks) { else { process.stdout.write(`WAMR interpreter ... \t`); elapsed = run_multiple_times(`${iwasm_gc} ${collect_benchmark_options(benchmark_options[prefix]?.wamr_option)} -f main ${prefix}.wasm`); - ts_times.push(elapsed); + wamr_interp_times.push(elapsed); console.log(`${elapsed.toFixed(2)}ms`); } @@ -202,7 +225,7 @@ for (let benchmark of benchmarks) { else { process.stdout.write(`WAMR AoT ... \t\t`); elapsed = run_multiple_times(`${iwasm_gc} ${collect_benchmark_options(benchmark_options[prefix]?.wamr_option)} -f main ${prefix}.aot`); - aot_times.push(elapsed); + wamr_aot_times.push(elapsed); console.log(`${elapsed.toFixed(2)}ms`); } @@ -212,7 +235,17 @@ for (let benchmark of benchmarks) { else { process.stdout.write(`QuickJS ... \t\t`); elapsed = run_multiple_times(`${qjs} ${js_file}`); - js_times.push(elapsed); + qjs_js_times.push(elapsed); + console.log(`${elapsed.toFixed(2)}ms`); + } + + if (specified_runtimes && !specified_runtimes.includes('node')) { + console.log(`\x1b[33mSkip Node due to argument filter.\x1b[0m`); + } + else { + process.stdout.write(`Node ... \t\t`); + elapsed = run_multiple_times(`${node_cmd} ${js_file}`); + v8_js_times.push(elapsed); console.log(`${elapsed.toFixed(2)}ms`); } @@ -229,38 +262,55 @@ console.log(`\x1b[32m====================== results ======================\x1b[0 let results = []; for (let i = 0; i < executed_benchmarks; i++) { - let ts_time = ts_times[i]; - let js_time = js_times[i]; - let aot_time = aot_times[i]; + let wamr_interp_time = wamr_interp_times[i]; + let qjs_js_time = qjs_js_times[i]; + let wamr_aot_time = wamr_aot_times[i]; + let v8_js_time = v8_js_times[i]; let r = { benchmark: prefixs[i] } - if (ts_time) { - r['WAMR_interpreter'] = ts_time.toFixed(2) + 'ms'; + if (wamr_interp_time) { + r['WAMR_interpreter'] = wamr_interp_time.toFixed(2) + 'ms'; } - if (aot_time) { - r['WAMR_aot'] = aot_time.toFixed(2) + 'ms'; + if (wamr_aot_time) { + r['WAMR_aot'] = wamr_aot_time.toFixed(2) + 'ms'; } - if (js_time) { - r['QuickJS'] = js_time.toFixed(2) + 'ms'; + if (qjs_js_time) { + r['QuickJS'] = qjs_js_time.toFixed(2) + 'ms'; } - if (ts_time && js_time) { - let ratio = ts_time / js_time; + if (v8_js_time) { + r['Node'] = v8_js_time.toFixed(2) + 'ms'; + } + + if (wamr_interp_time && qjs_js_time) { + let ratio = wamr_interp_time / qjs_js_time; let formatted_result = ratio.toFixed(2); r['WAMR_interpreter/qjs'] = formatted_result; } - if (aot_time && js_time) { - let ratio_aot = aot_time / js_time; + if (wamr_aot_time && qjs_js_time) { + let ratio_aot = wamr_aot_time / qjs_js_time; let formatted_result_aot = ratio_aot.toFixed(2); r['WAMR_aot/qjs'] = formatted_result_aot; } + if (wamr_interp_time && v8_js_time) { + let ratio = wamr_interp_time / v8_js_time; + let formatted_result = ratio.toFixed(2); + r['WAMR_interpreter/node'] = formatted_result; + } + + if (wamr_aot_time && v8_js_time) { + let ratio_aot = wamr_aot_time / v8_js_time; + let formatted_result_aot = ratio_aot.toFixed(2); + r['WAMR_aot/node'] = formatted_result_aot; + } + results.push(r); } diff --git a/tools/validate/run_module_on_node/import_object.js b/tools/validate/run_module/import_object.js similarity index 98% rename from tools/validate/run_module_on_node/import_object.js rename to tools/validate/run_module/import_object.js index d4483921..28f6ceb0 100644 --- a/tools/validate/run_module_on_node/import_object.js +++ b/tools/validate/run_module/import_object.js @@ -286,11 +286,11 @@ const importObject = { } }, env: { - console_log: (obj) => { + Console_log: (obj) => { /** TODO: cant log reference type variable */ console.log(obj); }, - console_constructor: (obj) => {}, + Console_constructor: (obj) => {}, strcmp(a, b) { let lhs = cstringToJsString(a); let rhs = cstringToJsString(b); @@ -298,6 +298,8 @@ const importObject = { }, setTimeout: (obj) => {}, clearTimeout: (obj) => {}, + malloc: (size)=>{}, + free: (size)=>{}, array_push_generic: (ctx, obj, elem) => {}, array_pop_f64: (ctx, obj) => {}, diff --git a/tools/validate/run_module_on_node/run_module_on_node.md b/tools/validate/run_module/readme.md similarity index 73% rename from tools/validate/run_module_on_node/run_module_on_node.md rename to tools/validate/run_module/readme.md index 93c629d9..74f3be20 100644 --- a/tools/validate/run_module_on_node/run_module_on_node.md +++ b/tools/validate/run_module/readme.md @@ -1,12 +1,14 @@ -# Run generated WASM module on Node.js +# Run generated WASM module -This document describes how to execute WASM module on Node.js. +This document describes how to execute WASM module on node.js and on chrome. > Note: Wasmnizer-ts follows the latest WasmGC spec, which requires `V8 v11.9+`, but the latest nodejs (v21.5.0) is using `V8 11.8.172.17`, so currently the generated WASM module can't execute on any nodejs releases. > If you do want to try on nodejs, you can reset to commit `94cf9929421d47a9976fa6edf74b25ef2a00ee12` to build the compiler, which is compatible to older V8 versions. -## Prerequisites +## Run module on node + +### Prerequisites - node.js version 20.0.0 or higher to enable support for `stringref` feature, node.js version 20.0 or higher is necessary. @@ -16,7 +18,7 @@ This document describes how to execute WASM module on Node.js. - `--experimental-wasm-gc`: This flag is required to enable support for the WASM GC feature. - `--experimental-wasm-stringref`: This flag is needed to enable support for the `stringref` feature. -## How to Run +### How to Run To run your WebAssembly file, use the following command: @@ -31,7 +33,7 @@ This document describes how to execute WASM module on Node.js. - `-f`: specify the exported WASM function you want to execute in Node.js. - `-s`: specify to execute the `_start` WASM function to initialize global variables if necessary. -## Example +### Example Here is an example. @@ -52,3 +54,14 @@ This document describes how to execute WASM module on Node.js. ``` it will output `1`. + +## Run module on chrome + +### Prerequisites +- Set chrome flags by `chrome://flags`, should set these flags as enabled: + - Experimental WebAssembly + - WebAssembly Garbage Collection + - WebAssembly Stringref + +### How to Run +Start a server, open the `run_module_on_chrome.html` on chrome, fill in with the wasm path, the wasm function name, and arguments(must be separated by commas), then click `submit` button, and the result will be print on the page. \ No newline at end of file diff --git a/tools/validate/run_module/run_module_on_chrome.html b/tools/validate/run_module/run_module_on_chrome.html new file mode 100644 index 00000000..9bb80a29 --- /dev/null +++ b/tools/validate/run_module/run_module_on_chrome.html @@ -0,0 +1,62 @@ + + + + + + + run wasm module + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+

+ +
+ + +

+

+ + \ No newline at end of file diff --git a/tools/validate/run_module/run_module_on_chrome.js b/tools/validate/run_module/run_module_on_chrome.js new file mode 100644 index 00000000..7e6b2ed8 --- /dev/null +++ b/tools/validate/run_module/run_module_on_chrome.js @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +import { importObject, setWasmMemory } from './import_object.js'; + +export function run_wasm_module(filePath, funcName, warmupTimes, runTarget, ...funcArgs) { + const parts = filePath.split("."); + const extension = parts[parts.length - 1]; + if (runTarget === 'js') { + if (extension !== 'js') { + const resultElement = document.getElementById('result'); + resultElement.innerHTML = `Error: filePath must end with ".js`; + } + fetch(filePath) + .then(response => response.text()) + .then(script => { + if (warmupTimes) { + for (let i = 0; i < parseInt(warmupTimes); i++) { + eval(script); + } + } + const start_time = performance.now(); + let res = eval(script); + if (funcName) { + res = window[funcName](...funcArgs); + } + const end_time = performance.now(); + if (typeof res !== 'object' || res === null) { + const resultElement = document.getElementById('result'); + resultElement.innerHTML = `The result is: ${res}`; + } + const timeElement = document.getElementById('time'); + timeElement.innerHTML = `Execution time is: ${end_time - start_time}`; + }); + } else if (runTarget === 'wasm') { + if (extension !== 'wasm') { + const resultElement = document.getElementById('result'); + resultElement.innerHTML = `Error: filePath must end with ".wasm`; + } + fetch(filePath) + .then((response) => response.arrayBuffer()) + .then((bytes) => WebAssembly.instantiate(bytes, importObject)) + .then((results) => { + const exports = results.instance.exports; + setWasmMemory(exports.default); + const startFunc = exports._entry; + const exportedFunc = exports[funcName]; + if (warmupTimes) { + for (let i = 0; i < parseInt(warmupTimes); i++) { + startFunc(); + exportedFunc(...funcArgs); + } + } + const start_time = performance.now(); + startFunc(); + const res = exportedFunc(...funcArgs); + const end_time = performance.now(); + if (typeof res !== 'object' || res === null) { + const resultElement = document.getElementById('result'); + resultElement.innerHTML = `The result is: ${res}`; + } + const timeElement = document.getElementById('time'); + timeElement.innerHTML = `Execution time is: ${end_time - start_time}`; + }); + } +} \ No newline at end of file diff --git a/tools/validate/run_module_on_node/run_module.js b/tools/validate/run_module/run_module_on_node.js similarity index 100% rename from tools/validate/run_module_on_node/run_module.js rename to tools/validate/run_module/run_module_on_node.js