-
Notifications
You must be signed in to change notification settings - Fork 541
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
Use llhttp through wasm #22
Comments
The performance of http-parser-js came up as part of a discussion regarding this library. Hence this issue. |
I’d be happy to, however llhttp is not part of the public API, and I would prefer not to use a binary addon. Wasm could be cool, but I do not know how it would perform. On top of of that, all of this inlines and it avoids one or more jump between the http parser (C) and client (JS). Tbh, I don’t expect a bump in speed, but I would be happy to be surprised. |
I have to say I'm impressed. Even with
Benchmark source code'use strict';
const Benchmark = require('benchmark');
const {Client} = require('undici');
const suite = new Benchmark.Suite();
const client = new Client('https://localhost:8081', {
tls: {
rejectUnauthorized: false
}
});
const options = {
path: '/',
method: 'GET'
};
// Benchmarking
suite.add('undici', {
defer: true,
fn: async deferred => {
const {body} = await client.request(options);
body.resume();
body.once('end', () => {
deferred.resolve();
});
}
}).on('cycle', event => {
console.log(String(event.target));
}).on('complete', function () {
console.log(`Fastest is ${this.filter('fastest').map('name')}`);
}).run(); The fun fact is that it's even faster than the native HTTP2 implementation:
|
Anyone of you know of anyone that might be interested in creating or helping out with a wasm build of llhttp that can be used as an alternative to http-parser-js? I hope it would not be too difficult if one is familiar with the wasm build flow. |
In would recommend to open an issue on https://github.com/nodejs/llhttp, there is also https://www.npmjs.com/package/llhttp which might be usable somehow? |
@szmarczak Would you mind running those benchmarks again against master? I'm curious how much of a difference recent improvements have made. EDIT: even better if you use |
master: |
#109 but it very often goes below 20k: |
ah wait I didn't use the newer API, lemme check it out |
client.stream(options, () => {
const obj = {
on: () => obj,
write: () => {},
end: () => deferred.resolve()
};
return obj;
});
client.stream(options, () => {
let buffered = '';
const obj = {
on: () => obj,
write: chunk => {
buffered += chunk;
},
end: () => deferred.resolve()
};
return obj;
});
That's a big improvement! |
I did a quick test and the package https://www.npmjs.com/package/llhttp can be used directly, so it seems wasm is not necessary. client-base ...
const { HTTP: HTTParser } = require('llhttp')
... Note: I don't know how much this task would take atm |
After some research I found this issue which seems to suggest a way to use this parser through Node directly, which I think it could be a better option. I did try those steps some time ago and faced the same errors. @mcollina , when you say
You mean you wouldn't like to try the approach suggested in the issue? |
I think using |
Yes.
Yes, though I don't see anyway good alternative at the moment. Unless someone starts investigating wasm or napi (I'm unfortunately not familiar with either). I think it might be worth it. We would need to keep an eye on major releases of Node though to make sure it doesn't break. I mean, we could stick with http-parser-js. The performance difference is not significant. I'm mostly concerned with the security/compliance part at this point. |
I'm good with the approach. |
Fixed in 2240cb0 (although not using wasm). |
I think technically you could built it without the wasi by manually including libc headers since it doesn't use any system apis, but imo its easier just to use the sdk anyway. |
Thanks! |
I did some experiments... using the wasi-sdk works like magic, thanks @devsnek! From the point of view of Undici, we are missing some key things that are provided by https://github.com/nodejs/node/blob/master/src/node_http_parser.cc, hooks into the parsing flow: This is doable with much more work apparently. |
Is it, hopefully, a matter of copy/paste the bits of Node into llhttp? |
I'm blocked. No idea how to build https://github.com/devsnek/llhttp/tree/wasm with https://github.com/WebAssembly/wasi-sdk Tried everything I can think of to make clang find the correct include path without success: /build/c/llhttp.c:7574:10: fatal error: 'stdlib.h' file not found
#include <stdlib.h>
^~~~~~~~~~
1 error generated.
./src/native/api.c:1:10: fatal error: 'stdlib.h' file not found
#include <stdlib.h>
^~~~~~~~~~
1 error generated.
./src/native/http.c:1:10: fatal error: 'stdio.h' file not found
#include <stdio.h>
^~~~~~~~~
1 error generated. I guess UNIX tooling is not my strongest skill. e.g. /Users/ronagy/GitHub/public/devsnek-llhttp/wasi-sdk-12.0/bin/clang -I /Users/ronagy/GitHub/public/devsnek-llhttp/wasi-sdk-12.0/lib/clang/11.0.0/include --sysroot=$WASI_ROOT/share/wasi-sysroot -target wasm32-unknown-wasi -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 ./build/c/*.c ./src/native/*.c -I./build -o ./build/llhttp.wasm |
Just thinking out loud here: to define I am trying to play with it using the @devsnek branch (thank you). I apologize if this is stuff known already, I am new to wasi. |
@ronag The way that works for me is downloading a |
Also, |
Yea. We need to remove consume/unconsume and just use So we go from:
to:
|
Does that mean that |
Correct. That logic should live after |
I managed to put together a small experiment for a parser in https://github.com/dnlup/llhttp/blob/undici_wasm/wasm.js. Besides the things we already mentioned. like:
One other thing I am seeing is the headers timeout, the headers max size & number. Do we need all of them implemented in the base parser? What strategy do you think is best for embedding the parser in |
headersTimeout not necessary, we already have it in ja I think, we can implement max size in js. |
@dnlup at you working on this with intention to open PR? |
it would be best if undici kept having no runtime dependencies. A contribution in the form of a script that generates the relative javascript and wasm files and populate a folder in this repo would be rad. (Bonus point if the script is run within a docker image, so the toolchain to generate those files is super easy). |
Yes. There's a lot of new stuff for me here, so I might not be fast, but I hope to complete it. Anyway, I'd be happy to help with this issue. |
Ok, just to check I understand correctly, the best approach would be:
This script would also have to work locally. Otherwise, there would be no way to tun undici |
I would just clone master and add our own files on top (if we need them)
I don't understand this. |
Sorry. I thought that the script should have to run on the CI and locally (while working on your machine). But now that I think about it, maybe the easiest solution, for now, would be just to run it locally. We need to be able to build the parser to use undici. |
A local run (via docker) would be fine... we'll commit the output to this repo. |
This was done |
Would it be possible and make sense to use https://github.com/nodejs/llhttp instead of http-parser-js? Maybe as an opt-in?
The text was updated successfully, but these errors were encountered: