diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 1c8eb5512d8..f8671679967 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v1 with: - node-version: 12 + node-version: 16 - name: Install Modules run: npm i - name: Run Benchmark @@ -30,7 +30,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v1 with: - node-version: 12 + node-version: 16 - name: Install Modules run: npm i - name: Run Benchmark diff --git a/.taprc b/.taprc index f923754ce1f..102aa5e8162 100644 --- a/.taprc +++ b/.taprc @@ -5,3 +5,4 @@ coverage: true expose-gc: true timeout: 60 check-coverage: false +node-arg: --experimental-wasm-simd diff --git a/README.md b/README.md index 3a69b339579..7f32081461c 100644 --- a/README.md +++ b/README.md @@ -17,32 +17,36 @@ npm i undici ## Benchmarks -AMD EPYC 7502P 32 Core, Node 15 +Node 16 The benchmark is a simple `hello world` [example](benchmarks/index.js) using a number of unix sockets (connections) with a pipelining depth of 10. ### Connections 1 -| Test | Samples | Result | Tolerance | Difference with slowest | -|---------------------|---------|----------------|-----------|-------------------------| -| http - no keepalive | 99 | 812 reqs/sec | ± 0.22 % | | -| http - keepalive | 99 | 819 reqs/sec | ± 0.20 % | + 0.82 % | -| undici - pipeline | 99 | 6632 reqs/sec | ± 0.63 % | + 716.73 % | -| undici - request | 99 | 6645 reqs/sec | ± 1.34 % | + 718.34 % | -| undici - stream | 99 | 7366 reqs/sec | ± 0.59 % | + 807.11 % | -| undici - dispatch | 99 | 7404 reqs/sec | ± 0.37 % | + 811.76 % | +| Tests | Samples | Result | Tolerance | Difference with slowest | +|---------------------|---------|---------------|-----------|-------------------------| +| http - no keepalive | 15 | 4.63 req/sec | ± 2.77 % | - | +| http - keepalive | 10 | 4.81 req/sec | ± 2.16 % | + 3.94 % | +| undici - stream | 25 | 62.22 req/sec | ± 2.67 % | + 1244.58 % | +| undici - dispatch | 15 | 64.33 req/sec | ± 2.47 % | + 1290.24 % | +| undici - request | 15 | 66.08 req/sec | ± 2.48 % | + 1327.88 % | +| undici - pipeline | 10 | 66.13 req/sec | ± 1.39 % | + 1329.08 % | ### Connections 50 -| Test | Samples | Result | Tolerance | Difference with slowest | -|---------------------|---------|----------------|-----------|-------------------------| -| http - no keepalive | 99 | 12968 reqs/sec | ± 1.86 % | | -| http - keepalive | 99 | 14745 reqs/sec | ± 1.59 % | + 13.70 % | -| undici - pipeline | 99 | 20051 reqs/sec | ± 2.34 % | + 54.62 % | -| undici - stream | 100 | 26456 reqs/sec | ± 3.50 % | + 104.00 % | -| undici - request | 99 | 29342 reqs/sec | ± 1.26 % | + 126.26 % | -| undici - dispatch | 99 | 35323 reqs/sec | ± 0.77 % | + 172.38 % | +| Tests | Samples | Result | Tolerance | Difference with slowest | +|---------------------|---------|------------------|-----------|-------------------------| +| http - no keepalive | 50 | 3546.49 req/sec | ± 2.90 % | - | +| http - keepalive | 15 | 5692.67 req/sec | ± 2.48 % | + 60.52 % | +| undici - pipeline | 25 | 8478.71 req/sec | ± 2.62 % | + 139.07 % | +| undici - request | 20 | 9766.66 req/sec | ± 2.79 % | + 175.39 % | +| undici - stream | 15 | 10109.74 req/sec | ± 2.94 % | + 185.06 % | +| undici - dispatch | 25 | 10949.73 req/sec | ± 2.54 % | + 208.75 % | + +#### Note + +The benchmarks have the [simd](https://github.com/WebAssembly/simd) feature enabled. ## Quick Start diff --git a/build/wasm.js b/build/wasm.js index ca16e5caabe..7c9c920c9c4 100644 --- a/build/wasm.js +++ b/build/wasm.js @@ -42,3 +42,25 @@ execSync(`${WASI_ROOT}/bin/clang \ ${join(WASM_SRC, 'src')}/*.c \ -I${join(WASM_SRC, 'include')} \ -o ${join(WASM_OUT, 'llhttp.wasm')}`, { stdio: 'inherit' }) + +// Build wasm simd binary +execSync(`${WASI_ROOT}/bin/clang \ + --sysroot=${WASI_ROOT}/share/wasi-sysroot \ + -target wasm32-unknown-wasi \ + -msimd128 \ + -Ofast \ + -fno-exceptions \ + -fvisibility=hidden \ + -mexec-model=reactor \ + -Wl,-error-limit=0 \ + -Wl,-O3 \ + -Wl,--lto-O3 \ + -Wl,--strip-all \ + -Wl,--allow-undefined \ + -Wl,--export-dynamic \ + -Wl,--export-table \ + -Wl,--export=malloc \ + -Wl,--export=free \ + ${join(WASM_SRC, 'src')}/*.c \ + -I${join(WASM_SRC, 'include')} \ + -o ${join(WASM_OUT, 'llhttp_simd.wasm')}`, { stdio: 'inherit' }) diff --git a/lib/client.js b/lib/client.js index 5e9b73d0a15..988d068397a 100644 --- a/lib/client.js +++ b/lib/client.js @@ -23,12 +23,6 @@ const { BodyTimeoutError } = require('./core/errors') -const { resolve } = require('path') -const { readFileSync } = require('fs') -const constants = require('./llhttp/constants') -const EMPTY_BUF = Buffer.alloc(0) -const mod = new WebAssembly.Module(readFileSync(resolve(__dirname, './llhttp/llhttp.wasm'))) - const { kUrl, kReset, @@ -386,6 +380,26 @@ class HTTPParserError extends Error { } } +let mod, build +const { resolve } = require('path') +const { readFileSync } = require('fs') +const constants = require('./llhttp/constants') +const EMPTY_BUF = Buffer.alloc(0) + +try { + build = resolve(__dirname, './llhttp/llhttp_simd.wasm') + const bin = readFileSync(build) + mod = new WebAssembly.Module(bin) +} catch (e) { + // We could check if the error was caused by the simd option not + // being enabled, but the occurring of this other error + // * https://github.com/emscripten-core/emscripten/issues/11495 + // got me to remove that check to avoid breaking Node 12. + build = resolve(__dirname, './llhttp/llhttp.wasm') + const bin = readFileSync(build) + mod = new WebAssembly.Module(bin) +} + const llhttp = new WebAssembly.Instance(mod, { env: { /* eslint-disable camelcase */ diff --git a/lib/llhttp/llhttp_simd.wasm b/lib/llhttp/llhttp_simd.wasm new file mode 100755 index 00000000000..52db42fb67d Binary files /dev/null and b/lib/llhttp/llhttp_simd.wasm differ diff --git a/package.json b/package.json index c40cc245be4..c856c47f4e4 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,10 @@ "coverage": "standard | snazzy && tap test/*.js", "coverage:ci": "npm run coverage -- --coverage-report=lcovonly", "bench": "concurrently -k -s first npm:bench:server npm:bench:run", + "bench:simd": "concurrently -k -s first npm:bench:server npm:bench:run:simd", "bench:server": "node benchmarks/server.js", "prebench:run": "node benchmarks/wait.js", - "bench:run": "CONNECTIONS=1 node benchmarks/benchmark.js && CONNECTIONS=50 node benchmarks/benchmark.js", + "bench:run": "CONNECTIONS=1 node --experimental-wasm-simd benchmarks/benchmark.js && CONNECTIONS=50 node --experimental-wasm-simd benchmarks/benchmark.js", "serve:website": "docsify serve .", "prepare": "husky install" },