From 679d456016e40aa5d9be686141829ce754c63461 Mon Sep 17 00:00:00 2001 From: JohnDonavon Date: Tue, 7 Mar 2023 16:01:24 -0800 Subject: [PATCH] add multithreading support --- .cargo/config | 6 ++ Cargo.lock | 20 ++++ wasm/Cargo.toml | 14 ++- wasm/rust-toolchain | 1 + wasm/src/lib.rs | 2 + website/src/workers/worker.js | 184 +++++++++++++++++----------------- website/webpack.config.js | 10 +- 7 files changed, 142 insertions(+), 95 deletions(-) create mode 100644 wasm/rust-toolchain diff --git a/.cargo/config b/.cargo/config index 7c6d59471..43ac15741 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,2 +1,8 @@ [target.'cfg(not(target_arch = "wasm32"))'] rustflags = ["-C", "target-cpu=native"] + +[target.wasm32-unknown-unknown] +rustflags = ["-C", "target-feature=+atomics,+bulk-memory,+mutable-globals"] + +[unstable] +build-std = ["panic_abort", "std"] \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 1824f9583..7087f073f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,6 +147,7 @@ dependencies = [ "js-sys", "rand", "rand_chacha", + "rayon", "serde", "serde-wasm-bindgen", "serde_json", @@ -154,6 +155,7 @@ dependencies = [ "snarkvm-synthesizer", "snarkvm-wasm", "wasm-bindgen", + "wasm-bindgen-rayon", "wasm-bindgen-test", "web-sys", ] @@ -2423,6 +2425,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spmc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a8428da277a8e3a15271d79943e80ccc2ef254e78813a166a08d65e4c3ece5" + [[package]] name = "strsim" version = "0.10.0" @@ -2914,6 +2922,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-rayon" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df87c67450805c305d3ae44a3ac537b0253d029153c25afc3ecd2edc36ccafb1" +dependencies = [ + "js-sys", + "rayon", + "spmc", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen-shared" version = "0.2.84" diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 85b2f621b..8d60d74b9 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -47,12 +47,13 @@ features = [ "preserve_order" ] path="../../snarkVM/synthesizer" version = "0.9.13" default-features = false -features = [ "wasm" ] +features = [ "wasm", "parallel", "web" ] [dependencies.snarkvm-console] path="../../snarkVM/console" version = "0.9.13" default-features = false +features = ["parallel"] [dependencies.snarkvm-wasm] path="../../snarkVM/wasm" @@ -82,6 +83,12 @@ version = "0.1.7" [dependencies.rand_chacha] version = "0.3.1" +[dependencies.wasm-bindgen-rayon] +version = "1.0" + +[dependencies.rayon] +version = "1.5" + [dev-dependencies.wasm-bindgen-test] version = "0.3.33" @@ -89,5 +96,8 @@ version = "0.3.33" opt-level = 3 lto = true +[package.metadata.wasm-pack.profile.debug] +wasm-opt = ["-O4"] + [package.metadata.wasm-pack.profile.release] -wasm-opt = ["-O4", "--fast-math"] +wasm-opt = ["-O4"] diff --git a/wasm/rust-toolchain b/wasm/rust-toolchain new file mode 100644 index 000000000..467f338ce --- /dev/null +++ b/wasm/rust-toolchain @@ -0,0 +1 @@ +nightly-2022-12-12 \ No newline at end of file diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index b5c18e7af..7df242c12 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -24,3 +24,5 @@ pub mod program; pub use program::*; pub(crate) mod types; + +pub use wasm_bindgen_rayon::init_thread_pool; \ No newline at end of file diff --git a/website/src/workers/worker.js b/website/src/workers/worker.js index 40215d51b..f72a85dae 100644 --- a/website/src/workers/worker.js +++ b/website/src/workers/worker.js @@ -1,103 +1,105 @@ -import "babel-polyfill"; +import init, * as aleo from '@aleohq/wasm'; + +await init(); + +await aleo.initThreadPool(navigator.hardwareConcurrency); let TRANSFER_KEY; let INCLUSION_KEY; -import("@aleohq/wasm").then(aleo => { - self.addEventListener("message", ev => { - // Load Transfer Prover Key - if (ev.data.type == 'ALEO_LOAD_TRANSFER_KEY') { - console.log('Web worker: Deserialize Transfer Key...'); - let startTime = performance.now(); - TRANSFER_KEY = aleo.ProvingKey.from_bytes(ev.data.transferProverBytes); - console.log(`Web worker: Deserialized transfer proving key Completed: ${performance.now() - startTime} ms`); - self.postMessage({ type: 'TRANSFER_KEY_DESERIALIZED' }); - } - // Load Inclusion Prover Key - else if (ev.data.type == 'ALEO_LOAD_INCLUSION_KEY') { - console.log('Web worker: Deserialize Inclusion Key...'); - console.log(ev.data); - let startTime = performance.now(); - INCLUSION_KEY = aleo.ProvingKey.from_bytes(ev.data.inclusionProverBytes); - console.log(`Web worker: Deserialized inclusion proving key Completed: ${performance.now() - startTime} ms`); - self.postMessage({ type: 'INCLUSION_KEY_DESERIALIZED' }); - } - // Create Transition - else if (ev.data.type == 'ALEO_CREATE_TRANSITION') { - const { - privateKey, - toAddress, - amount, - plaintext - } = ev.data; - const program = aleo.Program.credits(); +self.addEventListener("message", ev => { + // Load Transfer Prover Key + if (ev.data.type == 'ALEO_LOAD_TRANSFER_KEY') { + console.log('Web worker: Deserialize Transfer Key...'); + let startTime = performance.now(); + TRANSFER_KEY = aleo.ProvingKey.from_bytes(ev.data.transferProverBytes); + console.log(`Web worker: Deserialized transfer proving key Completed: ${performance.now() - startTime} ms`); + self.postMessage({ type: 'TRANSFER_KEY_DESERIALIZED' }); + } + // Load Inclusion Prover Key + else if (ev.data.type == 'ALEO_LOAD_INCLUSION_KEY') { + console.log('Web worker: Deserialize Inclusion Key...'); + console.log(ev.data); + let startTime = performance.now(); + INCLUSION_KEY = aleo.ProvingKey.from_bytes(ev.data.inclusionProverBytes); + console.log(`Web worker: Deserialized inclusion proving key Completed: ${performance.now() - startTime} ms`); + self.postMessage({ type: 'INCLUSION_KEY_DESERIALIZED' }); + } + // Create Transition + else if (ev.data.type == 'ALEO_CREATE_TRANSITION') { + const { + privateKey, + toAddress, + amount, + plaintext + } = ev.data; + const program = aleo.Program.credits(); - console.log('Web worker: Building Transition for program: ', program.id()); - let startTime = performance.now(); + console.log('Web worker: Building Transition for program: ', program.id()); + let startTime = performance.now(); - // Prepare inputs - const pK = aleo.PrivateKey.from_string(privateKey); - const inputs = JSON.stringify([plaintext, toAddress, `${amount}u64`]) + // Prepare inputs + const pK = aleo.PrivateKey.from_string(privateKey); + const inputs = JSON.stringify([plaintext, toAddress, `${amount}u64`]) - const transition = aleo.TransactionBuilder.build_transition( - program, - 'transfer', - inputs, - pK, - TRANSFER_KEY - ); - console.log(`Web worker: Transition Completed: ${performance.now() - startTime} ms`); - console.log(`Transition: ${transition}`); - self.postMessage({ type: 'TRANSITION_COMPLETED', transition }); - } - else if (ev.data.type == 'ALEO_CREATE_TRANSACTION') { - const { - transition, - inputIds, - stateRoot, - statePaths - } = ev.data; + const transition = aleo.TransactionBuilder.build_transition( + program, + 'transfer', + inputs, + pK, + TRANSFER_KEY + ); + console.log(`Web worker: Transition Completed: ${performance.now() - startTime} ms`); + console.log(`Transition: ${transition}`); + self.postMessage({ type: 'TRANSITION_COMPLETED', transition }); + } + else if (ev.data.type == 'ALEO_CREATE_TRANSACTION') { + const { + transition, + inputIds, + stateRoot, + statePaths + } = ev.data; - console.log('Web worker: Building Transaction...'); - let startTime = performance.now(); - let transitionParsed = JSON.parse(transition); - let inputIdsParsed = JSON.parse(inputIds); + console.log('Web worker: Building Transaction...'); + let startTime = performance.now(); + let transitionParsed = JSON.parse(transition); + let inputIdsParsed = JSON.parse(inputIds); - const transaction = aleo.TransactionBuilder.build_transaction( - INCLUSION_KEY, - JSON.stringify([{ transition: transitionParsed, input_ids: inputIdsParsed}]), - stateRoot, - statePaths - ); - console.log(`Web worker: Transaction Completed: ${performance.now() - startTime} ms`); - console.log(`Transaction: ${transaction}`); - self.postMessage({ type: 'TRANSACTION_COMPLETED', transaction }); - } - else if (ev.data.type == 'ALEO_VERIFY_TRANSACTION') { - const { - transaction, - transferVerifierBytes, - inclusionVerifierBytes - } = ev.data; - console.log('Web worker: Verifying Transaction...'); - let startTime = performance.now(); + const transaction = aleo.TransactionBuilder.build_transaction( + INCLUSION_KEY, + JSON.stringify([{ transition: transitionParsed, input_ids: inputIdsParsed}]), + stateRoot, + statePaths + ); + console.log(`Web worker: Transaction Completed: ${performance.now() - startTime} ms`); + console.log(`Transaction: ${transaction}`); + self.postMessage({ type: 'TRANSACTION_COMPLETED', transaction }); + } + else if (ev.data.type == 'ALEO_VERIFY_TRANSACTION') { + const { + transaction, + transferVerifierBytes, + inclusionVerifierBytes + } = ev.data; + console.log('Web worker: Verifying Transaction...'); + let startTime = performance.now(); - const program = aleo.Program.credits(); - const functionName = 'transfer'; - const transferVerifyingKey = aleo.VerifyingKey.from_bytes(transferVerifierBytes); - const inclusionVerifyingKey = aleo.VerifyingKey.from_bytes(inclusionVerifierBytes); + const program = aleo.Program.credits(); + const functionName = 'transfer'; + const transferVerifyingKey = aleo.VerifyingKey.from_bytes(transferVerifierBytes); + const inclusionVerifyingKey = aleo.VerifyingKey.from_bytes(inclusionVerifierBytes); - const verified = aleo.TransactionBuilder.verify_transaction( - transaction, - program, - functionName, - transferVerifyingKey, - inclusionVerifyingKey, - true, - true - ); - console.log(`Web worker: Transaction Verified: ${performance.now() - startTime} ms`); - console.log(verified); - } - }); + const verified = aleo.TransactionBuilder.verify_transaction( + transaction, + program, + functionName, + transferVerifyingKey, + inclusionVerifyingKey, + true, + true + ); + console.log(`Web worker: Transaction Verified: ${performance.now() - startTime} ms`); + console.log(verified); + } }); \ No newline at end of file diff --git a/website/webpack.config.js b/website/webpack.config.js index c2ac2b167..1049cc36d 100644 --- a/website/webpack.config.js +++ b/website/webpack.config.js @@ -12,6 +12,10 @@ const appConfig = { }, devServer: { port: 3000, + headers: { + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'require-corp' + }, }, module: { rules: [ @@ -34,7 +38,8 @@ const appConfig = { maxAssetSize: 8388608 }, experiments: { - asyncWebAssembly: true + asyncWebAssembly: true, + topLevelAwait: true }, devtool: 'source-map', } @@ -51,7 +56,8 @@ const workerConfig = { filename: "worker.js" }, experiments: { - asyncWebAssembly: true + asyncWebAssembly: true, + topLevelAwait: true }, devtool: 'source-map', };