diff --git a/Makefile b/Makefile index 02423043..d0233142 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ all: build/libllhttp.a build/libllhttp.so clean: rm -rf release/ rm -rf build/ + rm -rf test/tmp build/libllhttp.so: build/c/llhttp.o build/native/api.o \ build/native/http.o diff --git a/bench/index.ts b/bench/native.ts similarity index 100% rename from bench/index.ts rename to bench/native.ts diff --git a/bench/wasm.ts b/bench/wasm.ts new file mode 100644 index 00000000..820b6779 --- /dev/null +++ b/bench/wasm.ts @@ -0,0 +1,123 @@ +import { readFile } from "fs/promises"; +import * as constants from "../build/wasm/constants.js"; + +function http(input) { + return input + .trim() + .replaceAll(/^\s+/gm, "") + .replaceAll("\n", "") + .replaceAll("\\r", "\r") + .replaceAll("\\n", "\n"); +} + +function formatNumber(num, precision) { + return num + .toLocaleString("en-US", { + minimumFractionDigits: precision, + maximumFractionDigits: precision, + useGrouping: "always", + }) + .replaceAll(",", "_"); +} + +/* eslint-disable @stylistic/max-len */ +const samples = { + seanmonstar_httparse: http(` + GET /wp-content/uploads/2010/03/hello-kitty-darth-vader-pink.jpg HTTP/1.1\\r\\n + Host: www.kittyhell.com\\r\\n + User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; ja-JP-mac; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 Pathtraq/0.9\\r\\n + Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\\r\\n + Accept-Language: ja,en-us;q=0.7,en;q=0.3\\r\\n + Accept-Encoding: gzip,deflate\\r\\n + Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7\\r\\n + Keep-Alive: 115\\r\\n + Connection: keep-alive\\r\\n + Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.x; __utmz=xxxxxxxxx.xxxxxxxxxx.x.x.utmccn=(referral)|utmcsr=reader.livedoor.com|utmcct=/reader/|utmcmd=referral\\r\\n\\r\\n + `), + nodejs_http_parser: http(` + POST /joyent/http-parser HTTP/1.1\\r\\n + Host: github.com\\r\\n + DNT: 1\\r\\n + Accept-Encoding: gzip, deflate, sdch\\r\\n + Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\\r\\n + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) + AppleWebKit/537.36 (KHTML, like Gecko) + Chrome/39.0.2171.65 Safari/537.36\\r\\n + Accept: text/html,application/xhtml+xml,application/xml;q=0.9, + image/webp,*/*;q=0.8\\r\\n + Referer: https://github.com/joyent/http-parser\\r\\n + Connection: keep-alive\\r\\n + Transfer-Encoding: chunked\\r\\n + Cache-Control: max-age=0\\r\\n\\r\\nb\\r\\nhello world\\r\\n0\\r\\n\\r\\n + `) +}; +/* eslint-enable @stylistic/max-len */ + +async function main () { + const mod = await WebAssembly.compile( + await readFile(new URL("../build/wasm/llhttp.wasm", import.meta.url)) + ); + + const { exports: llhttp } = await WebAssembly.instantiate(mod, { + env: { + wasm_on_url() { + return 0; + }, + wasm_on_status() { + return 0; + }, + wasm_on_message_begin() { + return 0; + }, + wasm_on_header_field() { + return 0; + }, + wasm_on_header_value() { + return 0; + }, + wasm_on_headers_complete() { + return 0; + }, + wasm_on_body() { + return 0; + }, + wasm_on_message_complete() { + return 0; + }, + }, + }); + + for (const [ name, payload ] of Object.entries(samples)) { + const len = payload.length; + const iterations = 2 ** 33 / len; + const total = iterations * len; + + const parser = llhttp.llhttp_alloc(constants.TYPE.BOTH); + const ptr = llhttp.malloc(len); + new Uint8Array(llhttp.memory.buffer, ptr, len).set(Buffer.from(payload)); + + const start = process.hrtime.bigint(); + for (let i = 0; i < iterations; i++) { + llhttp.llhttp_execute(parser, ptr, len); + } + + llhttp.free(ptr); + llhttp.llhttp_free(ptr); + + const time = Number(process.hrtime.bigint() - start) / 1e9; + const bw = total / time; + + const label = name.padStart(21, " "); + const samples = formatNumber(iterations, 0).padStart(12); + const size = formatNumber(total / (1024 * 1024), 2).padStart(8); + const speed = formatNumber(bw / (1024 * 1024), 2).padStart(10); + const throughtput = formatNumber(iterations / time, 2).padStart(10); + const duration = formatNumber(time, 2).padStart(6); + + console.log( + `${label} | ${samples} samples | ${size} MB | ${speed} MB/s | ${throughtput} ops/sec | ${duration} s` + ); + } +} + +main() \ No newline at end of file diff --git a/bin/build_wasm.ts b/bin/build_wasm.ts index c3f8bc13..b3573324 100644 --- a/bin/build_wasm.ts +++ b/bin/build_wasm.ts @@ -40,8 +40,14 @@ if (process.argv[2] === '--docker') { if (process.platform === 'linux') { cmd += ` --user ${process.getuid!()}:${process.getegid!()}`; } + + if(process.env.NO_SSE) { + cmd += ` -e NO_SSE=${process.env.NO_SSE}` + } + cmd += ` --mount type=bind,source=${WASM_SRC}/build,target=/home/node/llhttp/build llhttp_wasm_builder npm run wasm`; + console.log(`> ${cmd}\n\n`); execSync(cmd, { cwd: WASM_SRC, stdio: 'inherit' }); process.exit(0); @@ -67,6 +73,7 @@ execSync( -fno-exceptions \ -fvisibility=hidden \ -mexec-model=reactor \ + ${process.env.NO_SSE ? '' : '-msimd128'} \ -Wl,-error-limit=0 \ -Wl,-O3 \ -Wl,--lto-O3 \ diff --git a/package.json b/package.json index 0f189ea9..192765e4 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "src" ], "scripts": { - "bench": "node --import tsx bench/index.ts", + "bench": "node --import tsx bench/native.ts", + "bench-wasm": "node --import tsx bench/wasm.ts", "build": "node --import tsx bin/generate.ts", "build-ts": "tsc", "prebuild-wasm": "npm run wasm -- --prebuild && npm run wasm -- --setup",