Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: enable wasm simd #735

Merged
merged 11 commits into from
Apr 27, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions .taprc
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ coverage: true
expose-gc: true
timeout: 60
check-coverage: false
node-arg: --experimental-wasm-simd
38 changes: 21 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
ronag marked this conversation as resolved.
Show resolved Hide resolved
|---------------------|---------|---------------|-----------|-------------------------|
| 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

Expand Down
22 changes: 22 additions & 0 deletions build/wasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' })
26 changes: 20 additions & 6 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 */
Expand Down
Binary file added lib/llhttp/llhttp_simd.wasm
Binary file not shown.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@
"build:wasm": "node build/wasm.js --docker",
"lint": "standard | snazzy",
"lint:fix": "standard --fix | snazzy",
"test": "tap test/*.js --no-coverage && jest test/jest/test",
"test": "tap test/*.js --no-coverage && jest test/jest/test",
"test:tdd": "tap test/*.js -w --no-coverage-report",
"test:typescript": "tsd",
"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"
},
Expand Down