From 162ba72e363743c926497d03066ada332a2d9150 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 3 Sep 2026 03:27:37 +0200 Subject: [PATCH] perf(wasm): speed up Zstd compression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LLVM's min-size profile keeps the inlining threshold too low for the Zstd hot path. Raise only that threshold for libdatadog-wasm, enable SIMD, and use Binaryen -O3 on the resulting module. Node 24.20.0 measured 7.2–20.9% faster compression for 1 MiB inputs and 4.9–13.6% for 4 MiB inputs. Brotli size decreases by 0.07%, while gzip size grows by 0.60%. --- crates/libdatadog-wasm/Cargo.toml | 6 ++- scripts/build-wasm.js | 23 +++++++++- test/build-wasm.js | 73 ++++++++++++++++++++++++++++++- 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/crates/libdatadog-wasm/Cargo.toml b/crates/libdatadog-wasm/Cargo.toml index 21a5563..f90951d 100644 --- a/crates/libdatadog-wasm/Cargo.toml +++ b/crates/libdatadog-wasm/Cargo.toml @@ -52,7 +52,7 @@ getrandom = { version = "0.2", features = ["js"] } # Shared release and profiling Cargo artifacts retain target feature metadata. # Disable GC because Binaryen can otherwise emit instructions that Node.js 18 does not support. wasm-opt = [ - "-Oz", + "-O3", "--enable-mutable-globals", "--enable-nontrapping-float-to-int", "--enable-bulk-memory", @@ -60,13 +60,14 @@ wasm-opt = [ "--enable-reference-types", "--enable-multivalue", "--disable-gc", + "--enable-simd", "--converge", ] [package.metadata.wasm-pack.profile.profiling] # Keep function names for crate attribution after wasm-opt runs in Linux CI. wasm-opt = [ - "-Oz", + "-O3", "--enable-mutable-globals", "--enable-nontrapping-float-to-int", "--enable-bulk-memory", @@ -74,6 +75,7 @@ wasm-opt = [ "--enable-reference-types", "--enable-multivalue", "--disable-gc", + "--enable-simd", "--converge", "--strip-dwarf", "-g", diff --git a/scripts/build-wasm.js b/scripts/build-wasm.js index 95f1f26..d39e07d 100644 --- a/scripts/build-wasm.js +++ b/scripts/build-wasm.js @@ -44,6 +44,10 @@ env[rustFlagsKey] = [env[rustFlagsKey], '-Zunstable-options', '-Cpanic=immediate .join(' ') env[rustupToolchainKey] = wasmRustToolchain +// Keep the min-size profile while relaxing only LLVM's inlining cap for the compression hot path. +// Rebenchmark this LLVM-internal threshold when updating rust-toolchain.toml. +const libdatadogWasmRustFlags = '-C target-feature=+simd128 -C llvm-args=-inline-threshold=45' + if (isMacOS) { const homebrewDir = env.HOMEBREW_DIR ?? '/opt/homebrew' const llvmDir = `${homebrewDir}/opt/llvm/` @@ -82,6 +86,23 @@ if (isMacOS) { function buildWasm (cratePath, outputDirectory, options = {}) { const { profiling = false, skipOptimization = false } = options const resolvedOutputDirectory = path.resolve(cratePath, outputDirectory) + const buildEnvironment = { ...env } + if (path.basename(cratePath) === 'libdatadog-wasm') { + if (buildEnvironment.CARGO_ENCODED_RUSTFLAGS === undefined) { + const rustFlagsName = buildEnvironment.RUSTFLAGS === undefined + ? 'CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS' + : 'RUSTFLAGS' + buildEnvironment[rustFlagsName] = [ + buildEnvironment[rustFlagsName], + libdatadogWasmRustFlags, + ].filter(Boolean).join(' ') + } else { + buildEnvironment.CARGO_ENCODED_RUSTFLAGS = [ + buildEnvironment.CARGO_ENCODED_RUSTFLAGS, + ...libdatadogWasmRustFlags.split(' '), + ].filter(Boolean).join('\x1F') + } + } fs.rmSync(resolvedOutputDirectory, { force: true, recursive: true }) const args = ['build'] if (profiling) args.push('--profiling') @@ -89,7 +110,7 @@ function buildWasm (cratePath, outputDirectory, options = {}) { args.push('--target', 'nodejs', cratePath, '--out-dir', resolvedOutputDirectory, '--', '-Z', 'build-std=std') childProcess.execFileSync('wasm-pack', args, { env: { - ...env, + ...buildEnvironment, // Keep optimized release and profiling builds on one Cargo artifact. // wasm-opt removes debug data from release output on supported platforms. ...((profiling || !skipOptimization) && { diff --git a/test/build-wasm.js b/test/build-wasm.js index 88f67ff..3d81926 100644 --- a/test/build-wasm.js +++ b/test/build-wasm.js @@ -9,12 +9,14 @@ const { test } = require('node:test') const buildScript = path.join(__dirname, '..', 'scripts', 'build-wasm.js') -test('configures and cleans WASM builds', () => { +test('configures WASM builds with scoped compiler flags', () => { const temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'build-wasm-')) const projectRoot = path.join(temporaryRoot, 'nested', 'repository') const binaryDirectory = path.join(temporaryRoot, 'bin') const homebrewDirectory = path.join(temporaryRoot, 'homebrew') const llvmDirectory = path.join(homebrewDirectory, 'opt', 'llvm', 'bin') + const existingRustFlags = '-C debuginfo=1' + const libdatadogWasmRustFlags = '-C target-feature=+simd128 -C llvm-args=-inline-threshold=45' try { fs.mkdirSync(projectRoot, { recursive: true }) @@ -33,6 +35,14 @@ test('configures and cleans WASM builds', () => { debug: process.env.CARGO_PROFILE_RELEASE_DEBUG, strip: process.env.CARGO_PROFILE_RELEASE_STRIP, })) + fs.writeFileSync( + path.join(outputDirectory, 'rustflags'), + JSON.stringify({ + encoded: process.env.CARGO_ENCODED_RUSTFLAGS, + standard: process.env.RUSTFLAGS, + target: process.env.CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS, + }), + ) `) for (const library of ['library_config', 'pipeline']) { @@ -61,6 +71,9 @@ test('configures and cleans WASM builds', () => { cwd: projectRoot, env: { ...env, + CARGO_ENCODED_RUSTFLAGS: undefined, + RUSTFLAGS: undefined, + CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS: existingRustFlags, HOMEBREW_DIR: homebrewDirectory, PATH: `${binaryDirectory}${path.delimiter}${process.env.PATH}`, }, @@ -86,6 +99,64 @@ test('configures and cleans WASM builds', () => { assert.deepStrictEqual(environment, os.platform() === 'darwin' ? {} : { debug: 'true', strip: 'false' }) + const rustFlagsFile = fs.readFileSync(path.join(outputDirectory, 'rustflags'), 'utf8') + const rustFlags = JSON.parse(rustFlagsFile) + assert.strictEqual(rustFlags.target, existingRustFlags) + } + + const crateDirectory = path.join(projectRoot, 'crates', 'libdatadog-wasm') + fs.mkdirSync(crateDirectory, { recursive: true }) + + const encodedExistingRustFlags = existingRustFlags.replaceAll(' ', '\x1F') + const encodedLibdatadogWasmRustFlags = libdatadogWasmRustFlags.replaceAll(' ', '\x1F') + const wasmToolchainRustFlags = '-Zunstable-options -Cpanic=immediate-abort' + const rustFlagCases = [ + { + environment: { CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS: existingRustFlags }, + expected: `${wasmToolchainRustFlags} ${libdatadogWasmRustFlags}`, + name: 'target', + field: 'standard', + }, + { + environment: { RUSTFLAGS: existingRustFlags }, + expected: `${existingRustFlags} ${wasmToolchainRustFlags} ${libdatadogWasmRustFlags}`, + name: 'standard', + field: 'standard', + }, + { + environment: { CARGO_ENCODED_RUSTFLAGS: encodedExistingRustFlags }, + expected: `${encodedExistingRustFlags}\x1F${encodedLibdatadogWasmRustFlags}`, + name: 'encoded', + field: 'encoded', + }, + ] + + for (const { environment, expected, name, field } of rustFlagCases) { + const outputDirectory = path.join(projectRoot, 'prebuilds', `libdatadog-wasm-${name}`) + execFileSync(process.execPath, [buildScript, crateDirectory, outputDirectory], { + cwd: projectRoot, + env: { + ...process.env, + CARGO_ENCODED_RUSTFLAGS: undefined, + CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS: undefined, + RUSTFLAGS: undefined, + ...environment, + HOMEBREW_DIR: homebrewDirectory, + PATH: `${binaryDirectory}${path.delimiter}${process.env.PATH}`, + }, + stdio: 'pipe', + }) + + const rustFlags = JSON.parse( + fs.readFileSync(path.join(outputDirectory, 'rustflags'), 'utf8'), + ) + assert.strictEqual( + rustFlags[field], + expected, + ) + if (field === 'standard') { + assert.strictEqual(rustFlags.target, name === 'target' ? existingRustFlags : undefined) + } } const profilingCrate = path.join(projectRoot, 'crates', 'profiling')