Http2 x http1 benchmark #5084
Replies: 5 comments 1 reply
-
|
can you provide a repo with the benchmarks you are attempting? |
Beta Was this translation helpful? Give feedback.
-
|
I'll prepare one this weekend! But It consists just in the provided js file with latest undici and axios version installed. I'm running it on latest NodeJs 24, too. Also, I took a look at both, axios and undici http2 conn management. Axios does not use a connection pool, at least at first glance. That's why I've setup undici to 1 connection, so it wouldn't pool conns, too. That was the best setup I could figure out for this bench. Finally, I've ran it in inside wsl. I'll run it in a pipeline after I create the repo you asked to see if my local machine affects the bench anyhow |
Beta Was this translation helpful? Give feedback.
-
|
@metcoder95 I've created the repository as requested. Resutls keeps consistent between node versions: undici beat axios by a large difference when using http1, but http2 axios is consistently faster https://github.com/Farenheith/undici-benchmark/actions/runs/24944781831/job/73044257456 |
Beta Was this translation helpful? Give feedback.
-
|
Thanks for putting together the repro repo — I took a closer look and one thing stands out immediately on the Undici side:
get: (url) => {
const controller = new AbortController()
return undici.fetch(url, {
method: 'GET',
dispatcher: undiciAgentH2,
headers: new Headers({
Connection: 'keep-alive'
}),
signal: controller.signal
}).then((result) => {
if (!result.ok) throw new Error(`Request failed with status ${result.status}`)
return result
}).then((res) => res.text())
}Axios is not doing the equivalent in this benchmark. The repo just calls I tried a reduced harness with the same basic H2 workload, but changing the Undici calls as follows: Current repo-style Undici fetchawait undici.fetch(url, {
dispatcher: agent,
headers: new Headers({ Connection: 'keep-alive' }),
signal: new AbortController().signal
})Minimal Undici fetchawait undici.fetch(url, {
dispatcher: agent
})Undici requestawait undici.request(url, {
dispatcher: agent
})In my runs, the results looked like this:
So just removing the extra per-request setup on the That’s why I’d be careful about reading the current repo result as:
What it seems to show more specifically is:
One especially important point is that the Also, I checked connection usage on the H2 side: both clients were using 1 HTTP/2 session with 300 streams, so this does not appear to be a connection-count difference. The asymmetry in per-request setup looks like a more plausible explanation for at least part of the delta. I think a useful next step would be to benchmark these three side by side in the repo itself:
That would help separate fetch-layer overhead from transport performance. |
Beta Was this translation helpful? Give feedback.
-
|
@mcollina, thank you for your feedback about my benchmark. AbortController was used because axios offers timeout control in its external api, so I wanted to make a fair comparison between both as handling request timeout is essential in most cases, but indeed I was using the wrong tool for the job; I should've used AbortSignal directly. Fixed it. Keep-alive headers are also pointless when using http2, so I removed it, too, from all h2 cases. I've also improved the benchmark, adding the cases you suggested. Here are the new results: https://github.com/Farenheith/undici-benchmark/actions/runs/24960277630/job/73085633502 What bugged me a little bit is that request + AbortSignal is consistently better than pure request, which doesn't make any sense, as minimal fetch is faster than axios, and fetch + AbortSignal is slower. So, I've changed another thing: I've removed The results are still the same. https://github.com/Farenheith/undici-benchmark/actions/runs/24960556515/job/73086342693 So, somehow AbortSignal has some impact in fetch, but in request, it makes it a bit faster? I know the difference is marginal, but I've rerun this many times locally, and the rank doesn't change. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi, I was just exploring some benchmarks to try to justify at my company whether or not to use http2 in internal microservices. We're also in a movement to remove axios usage and adopt native fetch.
Of course, http2 can only be achieved with native fetch right now by using undici agent, but soon it'll be native as well (Node 26).
All that said, I created the following benchmark:
http2-benchmark-undici.js
The results were partially unexpected for me:
I even tried to quickly write a custom Agent for Undici, but this is not so trivial, so I hung that possibility for later.
Axios setup for http2 was the default one, just setting up httpVersion, while undici agent I've set up like this:
I've tried to find a configuration where I could extract most of undici, but the most relevant one for this test was to set up connections to 1, removing pool overhead.
This is not a problem with the lib. Performance is consistently better with much less variance in response time when using http2 compared to http1, as expected, and this still justifies trying that out with my colleagues, but I wonder if there's room for improvement in HTTP/2 requests, or if this difference is justified by extra security measures undici takes
Beta Was this translation helpful? Give feedback.
All reactions