node-tls-clientLatest version on npm

Performance

How 2.1.0, 2.2.0 and the node-tls-client binary run requests, and how their speed, CPU and memory compare.

5,000 requests at once
13.1 s → 0.26 s
51× faster · with the node-tls-client binary
CPU per request
1,327 µs → 494 µs
2.7× less CPU · 64 in flight, default binary
Memory, 64 in flight
372 MB → 86 MB
4.3× less memory · peak, default binary

How requests run

The TLS work is done by tls-client, a native library. With its official binary, a request is one call that returns only when the response has arrived, so a thread waits on every request in flight. What changed between versions is that thread:

2.1.02.2.02.2.0 + node-tls-client binary
Each request in flight holdsa worker thread, each with its own copy of the JavaScript enginea thread from Node's libuv threadpoolno thread
Requests running at onceCPU cores × 2, fixedUV_THREADPOOL_SIZE: CPU cores × 2 by default, up to 1024every request you start, as with node-fetch
  • 2.1.0 passed every request and response between threads and ran a JavaScript engine per worker. That is where most of its CPU and memory went.
  • 2.2.0 makes the same call from the main thread, and Node runs it on a threadpool thread. Raise the limit with UV_THREADPOOL_SIZE (see Concurrency); 2.1.0 had no setting for it.
  • The node-tls-client binary is our build of tls-client. Its call returns at once, and the library calls back when responses arrive, so requests in flight hold no thread. Opt in with initTLS({ binary: "node-tls-client" }).

Benchmarks

The three columns use the same browser profile and options. node-fetch 2.7.0 is in the table for reference.

Requests in flight

Against a server that answers in 50 ms. 2.1.0 and 2.2.0 both run 20 requests at once on the test machine, so they stop at about 385 requests per second and their lines overlap. With the node-tls-client binary, throughput keeps growing with requests in flight.

2.1.02.2.02.2.0 + node-tls-client binary
05K10K15K20K116642561,024Requests in flight15,432 req/s
Requests per second (higher is better) at each number of requests in flight, against a server that answers in 50 ms. Hover or focus the chart for values; all of them are in the table below.

Bursts

5,000 requests started at once, 50 ms server: time until all have finished (lower is better)
2.1.013.1 s2.2.013.0 s2.2.0 + node-tls-client binary0.26 s
2,000 HTTPS requests over HTTP/1.1 started at once, 50 ms server: time until all have finished (lower is better)
2.1.05.21 s2.2.05.20 s2.2.0 + node-tls-client binary0.47 s
2,000 HTTPS requests over HTTP/2 started at once, 50 ms server: time until all have finished (lower is better)
2.1.05.26 s2.2.05.21 s2.2.0 + node-tls-client binary0.10 s

Over HTTP/1.1, each request opens its own connection. Over HTTP/2, the requests share one connection, so the node-tls-client binary finishes in 0.10 s instead of 0.47 s. node-fetch doesn't support HTTP/2, so the table below compares it on HTTP/1.1 only. In two of three HTTP/1.1 runs, node-fetch lost connections (401 of 6,000 requests failed with ETIMEDOUT) and took about 8 s; its clean run took 0.8 s. The test server's queue of waiting connections holds 128 on macOS by default, and node-fetch overflowed it; the tls-client builds never did.

CPU and memory

CPU time per request, 64 in flight, 50 ms server (lower is better)
2.1.01,327 µs2.2.0494 µs2.2.0 + node-tls-client binary314 µs
Peak memory, 64 in flight, 50 ms server (lower is better)
2.1.0372 MB2.2.086 MB2.2.0 + node-tls-client binary89 MB

2.1.0 used the most CPU at moderate load: with 16 requests in flight it kept about 3 CPU cores busy (9.8 ms per request, against 0.56 ms in 2.2.0), because its pool stopped idle worker threads and started new ones as requests came and went.

With the node-tls-client binary, memory grows with requests in flight instead of stopping at the thread count: see the burst rows in the table below, and Memory.

All results

Medians of three runs.

Scenario2.1.02.2.02.2.0 + node-tls-client binarynode-fetch 2.7.0
1 in flight, 50 ms server: requests/s19 req/s19 req/s19 req/s19 req/s
16 in flight, 50 ms server: requests/s305 req/s309 req/s306 req/s309 req/s
64 in flight, 50 ms server: requests/s377 req/s385 req/s1,207 req/s1,228 req/s
256 in flight, 50 ms server: requests/s383 req/s385 req/s4,735 req/s4,597 req/s
1,024 in flight, 50 ms server: requests/s383 req/s384 req/s15,432 req/s14,339 req/s
5,000 at once, 50 ms server: time to finish13.1 s13.0 s0.26 s0.43 s
5,000 at once, 50 ms server: peak memory468 MB131 MB349 MB278 MB
16 in flight, 50 ms server: CPU per request9,833 µs564 µs496 µs418 µs
64 in flight, 50 ms server: CPU per request1,327 µs494 µs314 µs235 µs
64 in flight, 50 ms server: peak memory372 MB86 MB89 MB84 MB
One at a time, instant server: median latency1.5 ms1.4 ms1.5 ms1.4 ms
64 in flight, instant server: requests/s10,068 req/s10,302 req/s31,343 req/s29,790 req/s
64 in flight, instant server: CPU per request364 µs240 µs125 µs47 µs
HTTPS (HTTP/1.1), 64 in flight, 50 ms server: requests/s383 req/s382 req/s1,219 req/s1,226 req/s
HTTPS (HTTP/1.1), 2,000 at once, 50 ms server: time to finish5.21 s5.20 s0.47 s8.08 s (401 of 6,000 requests failed)
HTTPS (HTTP/2), 64 in flight, 50 ms server: requests/s378 req/s382 req/s1,202 req/snot supported
HTTPS (HTTP/2), 2,000 at once, 50 ms server: time to finish5.26 s5.21 s0.10 snot supported

How this was measured

  • Apple M5 (10 cores, 16 GB), macOS 26.6, Node.js 26.7.0.
  • A local server in a separate process answered each request after 50 ms (or at once, for the CPU and latency rows), over HTTP/1.1. The HTTPS rows ran twice: over HTTP/1.1 with every client, and over HTTP/2 without node-fetch, which doesn't support it.
  • Each run was a fresh process with one Session and each client's default settings: 20 workers in 2.1.0, and UV_THREADPOOL_SIZE 20 in 2.2.0. Three runs per cell, clients in rotating order, with leftover sockets drained between runs.
  • Native libraries: 2.1.0 and 2.2.0's default binary used tls-client 1.16.0; the node-tls-client binary was built from tls-client 1.18.0.
  • The harness is bench/ in the repository.

On this page