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: Optimize Header Validation #3994

Open
PandaWorker opened this issue Jan 8, 2025 · 4 comments
Open

perf: Optimize Header Validation #3994

PandaWorker opened this issue Jan 8, 2025 · 4 comments
Labels
enhancement New feature or request

Comments

@PandaWorker
Copy link

The current implementation of header validation in the codebase uses Uint8Array to store character validity maps for HTTP tokens, URIs, and header values. While this approach works, it can be further optimized using bitmasking to reduce memory usage and improve performance.

function isValidHTTPToken (characters) {

const TOKEN_MAP = new Uint8Array([
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]);

const URI_MAP = new Uint8Array([
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
]);

const HEADER_VALUE_MAP = new Uint8Array([
	0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
]);


export function isHTTPToken(c: number) {
	return c < 256 && TOKEN_MAP[c] === 1;
}

export function isHeaderValueToken(c: number) {
	return c < 256 && HEADER_VALUE_MAP[c] === 1;
}

export function isURIToken(c: number) {
	return c < 256 && URI_MAP[c] === 1;
}

export function isValidHeaderName(name: string) {
	for (let i = 0, len = name.length; i < len; i++) {
		if (!isHTTPToken(name.charCodeAt(i))) return false;
	}
	return true;
}

export function isValidHeaderValue(value: string) {
	for (let i = 0, len = value.length; i < len; i++) {
		if (!isHeaderValueToken(value.charCodeAt(i))) return false;
	}
	return true;
}

// undici 
const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;

export function isValidHeaderValue2(characters: string): boolean {
	return !headerCharRegex.test(characters);
}

export function isTokenCharCode(c: number) {
	switch (c) {
		case 0x22:
		case 0x28:
		case 0x29:
		case 0x2c:
		case 0x2f:
		case 0x3a:
		case 0x3b:
		case 0x3c:
		case 0x3d:
		case 0x3e:
		case 0x3f:
		case 0x40:
		case 0x5b:
		case 0x5c:
		case 0x5d:
		case 0x7b:
		case 0x7d:
			// DQUOTE and "(),/:;<=>?@[\]{}"
			return false;
		default:
			// VCHAR %x21-7E
			return c >= 0x21 && c <= 0x7e;
	}
}

export function isValidHTTPToken(characters: string) {
	for (let i = 0; i < characters.length; ++i) {
		if (!isTokenCharCode(characters.charCodeAt(i))) {
			return false;
		}
	}
	return true;
}

Benchmarks

import * as utils from './constants.ts';

async function bench() {
	const { bench, run, summary } = await import('mitata');

	summary(() => {
		bench('isValidHeaderValue one char', () => {
			for (let index = 0; index < 512; index++) {
				utils.isValidHeaderValue(String.fromCharCode(index));
			}
		});

		bench('undici.isValidHeaderValue one char', () => {
			for (let index = 0; index < 512; index++) {
				utils.isValidHeaderValue2(String.fromCharCode(index));
			}
		});
	});

	summary(() => {
		bench('isValidHeaderValue', () => {
			for (let index = 0; index < 512; index++) {
				utils.isValidHeaderValue('accessToken=HFVTGWBNJNMDJNDJNDHBDHJDD123');
			}
		});

		bench('undici.isValidHeaderValue (re)', () => {
			for (let index = 0; index < 512; index++) {
				utils.isValidHeaderValue2('accessToken=HFVTGWBNJNMDJNDJNDHBDHJDD123');
			}
		});
	});


	summary(() => {
		bench('isHTTPToken', () => {
			for (let index = 0; index < 512; index++) {
				utils.isHTTPToken(index);
			}
		});

		bench('undici.isTokenCharCode', () => {
			for (let index = 0; index < 512; index++) {
				utils.isTokenCharCode(index);
			}
		});
	});

	summary(() => {
		bench('isValidHeaderName', () => {
			for (let index = 0; index < 512; index++) {
				utils.isValidHeaderName('X-Authorization-User-Id');
			}
		});

		bench('undici.isValidHTTPToken', () => {
			for (let index = 0; index < 512; index++) {
				utils.isValidHTTPToken('X-Authorization-User-Id');
			}
		});
	});

	summary(() => {
		bench('isValidHeaderName invalid', () => {
			for (let index = 0; index < 512; index++) {
				utils.isValidHeaderName('X-Authorization-User\n-Id');
			}
		});

		bench('undici.isValidHTTPToken invalid', () => {
			for (let index = 0; index < 512; index++) {
				utils.isValidHTTPToken('X-Authorization-User\n-Id');
			}
		});
	});

	for (let index = 0; index < 256; index++) {
		const char = String.fromCharCode(index);
		const [a, b] = [utils.isValidHeaderName(char), utils.isValidHTTPToken(char)];
		const [c, d] = [utils.isValidHeaderValue(char), utils.isValidHeaderValue2(char)];

		if ((a !== b) || (c !== d)) {
			console.warn({ index, char, a, b, c, d});
		}
	}

	run();
}

bench();

Results

clk: ~3.04 GHz
cpu: Apple M1 Max
runtime: node 22.12.0 (arm64-darwin)

benchmark                   avg (min … max) p75   p99    (min … top 1%)
------------------------------------------- -------------------------------
isValidHeaderValue one char    1.34 µs/iter   1.38 µs    ▄        █  ▂     
                        (1.26 µs … 1.48 µs)   1.43 µs ▅█▅█▅▅▂▁▁▂▁▂█▅██▅▂▁▂▂
undici.isValidHeaderValue ..   7.40 µs/iter   7.33 µs ▂█▂                  
                        (7.26 µs … 8.19 µs)   7.82 µs ███▇▁▄▁▁▁▁▁▁▁▄▁▁▁▁▁▁▄

summary
  isValidHeaderValue one char
   5.53x faster than undici.isValidHeaderValue one char

------------------------------------------- -------------------------------
isValidHeaderValue            19.84 µs/iter  20.16 µs  █                 █ 
                      (19.41 µs … 20.27 µs)  20.20 µs ▆█▁▆▁▁▁▁▁▁▁▁▆▆▁▁▁▁▁█▆
undici.isValidHeaderValue ..  20.48 µs/iter  20.44 µs  █                   
                      (20.36 µs … 21.11 µs)  20.70 µs ▅██▅▁▅▁▁▅▁▁▁▁▁▁▁▁▁▁▁▅

summary
  isValidHeaderValue
   1.03x faster than undici.isValidHeaderValue (re)

------------------------------------------- -------------------------------
isHTTPToken                  350.50 ns/iter 373.16 ns                    █ 
                    (182.86 ns … 389.65 ns) 381.80 ns ▃▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▂
undici.isTokenCharCode       732.03 ns/iter 747.38 ns              █       
                    (676.33 ns … 796.38 ns) 786.35 ns ▅▂▁▁▁▁▁▁▁▁▁▁▁█▂▁▁▁▁▁▁

summary
  isHTTPToken
   2.09x faster than undici.isTokenCharCode

------------------------------------------- -------------------------------
isValidHeaderName             13.53 µs/iter  14.96 µs ▇             █      
                      (11.21 µs … 68.92 µs)  16.46 µs █▂▂▁▁▁▁▁▁▁▁▁▁▁█▂▃▁▂▁▁
undici.isValidHTTPToken       22.95 µs/iter  23.04 µs █ ▄                  
                      (21.96 µs … 83.75 µs)  31.96 µs █▂█▃▃▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

summary
  isValidHeaderName
   1.7x faster than undici.isValidHTTPToken

------------------------------------------- -------------------------------
isValidHeaderName invalid     10.34 µs/iter  10.41 µs █ █                  
                      (10.18 µs … 10.53 µs)  10.51 µs █▁█▁▁▁██▁▁▁▁███▁▁▁█▁█
undici.isValidHTTPToken in..  20.10 µs/iter  20.36 µs  ▃                  █
                      (19.62 µs … 20.82 µs)  20.37 µs ▆█▁▁▆▁▁▁▁▁▆▁▁▁▆▆▁▁▆▁█

summary
  isValidHeaderName invalid
   1.94x faster than undici.isValidHTTPToken invalid
@PandaWorker PandaWorker added the enhancement New feature or request label Jan 8, 2025
@PandaWorker
Copy link
Author

Regular expressions start winning after about 20 characters, so you can use different implementations based on the number of characters.

export function isValidHeaderValue(value: string) {
	for (let i = 0, len = value.length; i < len; i++) {
		if (!isHeaderValueToken(value.charCodeAt(i))) return false;
	}
	return true;
}

export function isValidHeaderValueOptimized(value: string){
	return value.length >= 20 ? isValidHeaderValue2(value) : isValidHeaderValue(value);
}

// undici 
const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;

export function isValidHeaderValue2(characters: string): boolean {
	return !headerCharRegex.test(characters);
}
import { bench, compact, lineplot, run } from 'mitata';
import * as utils from './constants.ts'; //

async function benchmark() {

	lineplot(() => {
		compact(() => {
			bench('isValidHeaderValue($size)', function* (state) {
				const size = state.get('size');
				yield () => utils.isValidHeaderValue('a'.repeat(size));
			}).range('size', 1, 512, 2);
	
			bench('isValidHeaderValue2($size)', function* (state) {
				const size = state.get('size');
	
				yield () => utils.isValidHeaderValue2('a'.repeat(size));
			}).range('size', 1, 512, 2).highlight('green');

			bench('isValidHeaderValueOp($size)', function* (state) {
				const size = state.get('size');
	
				yield () => utils.isValidHeaderValueOptimized('a'.repeat(size));
			}).range('size', 1, 512, 2).highlight('blue');
		});
	});

	run();
}

benchmark();
clk: ~2.98 GHz
cpu: Apple M1 Max
runtime: node 22.12.0 (arm64-darwin)

benchmark                   avg (min … max) p75   p99    (min … top 1%)
------------------------------------------- -------------------------------
isValidHeaderValue(1)          6.44 ns/iter   6.09 ns  13.39 ns █▂▁▁▁▁▁▁▁▁▁
isValidHeaderValue(2)         13.48 ns/iter  13.01 ns  36.89 ns █▁▁▁▁▁▁▁▁▁▁
isValidHeaderValue(4)         22.71 ns/iter  22.59 ns  42.43 ns █▂▁▁▁▁▁▁▁▁▁
isValidHeaderValue(8)         35.61 ns/iter  35.10 ns  55.83 ns █▂▁▁▁▁▁▁▁▁▁
isValidHeaderValue(16)        89.79 ns/iter  89.51 ns 111.66 ns █▄▁▁▁▁▁▁▁▁▁
isValidHeaderValue(32)       148.24 ns/iter 146.65 ns 180.44 ns ▂▇█▂▁▁▁▁▁▁▁
isValidHeaderValue(64)       244.00 ns/iter 244.31 ns 272.60 ns ▂▁▁▃█▁▁▁▁▁▁
isValidHeaderValue(128)      430.87 ns/iter 434.69 ns 489.22 ns ▂▁▁▁█▂▂▁▁▁▁
isValidHeaderValue(256)      784.24 ns/iter 801.97 ns 831.61 ns ▃▂▁▁▁▁▁█▁▂▁
isValidHeaderValue(512)        1.48 µs/iter   1.54 µs   1.58 µs ▇▃▂▁▁▁▁▁█▂▂
isValidHeaderValue2(1)        14.00 ns/iter  13.80 ns  18.68 ns █▁▁▁▁▁▁▁▁▁▁
isValidHeaderValue2(2)        19.95 ns/iter  19.53 ns  25.35 ns █▂▁▁▁▁▁▁▁▁▁
isValidHeaderValue2(4)        27.76 ns/iter  27.76 ns  47.04 ns █▂▁▁▁▁▁▁▁▁▁
isValidHeaderValue2(8)        37.00 ns/iter  36.50 ns  57.35 ns █▂▁▁▁▁▁▁▁▁▁
isValidHeaderValue2(16)       97.36 ns/iter  96.73 ns 120.33 ns █▃▁▁▁▁▁▁▁▁▁
isValidHeaderValue2(32)      119.66 ns/iter 120.23 ns 162.14 ns █▅▂▁▁▁▁▁▁▁▁
isValidHeaderValue2(64)      163.11 ns/iter 163.44 ns 194.48 ns █▃▂▁▁▁▁▁▁▁▁
isValidHeaderValue2(128)     229.44 ns/iter 226.66 ns 311.33 ns █▄▂▁▁▁▁▁▁▁▁
isValidHeaderValue2(256)     337.58 ns/iter 339.38 ns 369.10 ns ▄█▅▃▂▁▂▂▂▁▁
isValidHeaderValue2(512)     548.84 ns/iter 551.52 ns 575.91 ns ▅█▄▂▁▁▂▃▂▁▁
isValidHeaderValueOp(1)        6.12 ns/iter   6.06 ns   7.03 ns █▂▁▁▁▁▁▁▁▁▁
isValidHeaderValueOp(2)       13.20 ns/iter  12.95 ns  17.33 ns █▂▁▁▁▁▁▁▁▁▁
isValidHeaderValueOp(4)       22.80 ns/iter  22.42 ns  42.80 ns █▁▁▁▁▁▁▁▁▁▁
isValidHeaderValueOp(8)       37.17 ns/iter  37.28 ns  67.53 ns █▄▁▁▁▁▁▁▁▁▁
isValidHeaderValueOp(16)      93.46 ns/iter  93.99 ns 131.66 ns ██▃▂▁▁▁▁▁▁▁
isValidHeaderValueOp(32)     118.41 ns/iter 118.92 ns 147.88 ns █▄▂▁▁▁▁▁▁▁▁
isValidHeaderValueOp(64)     166.91 ns/iter 164.30 ns 235.86 ns █▃▂▁▁▁▁▁▁▁▁
isValidHeaderValueOp(128)    227.26 ns/iter 229.28 ns 268.29 ns ▆▅█▃▂▁▁▁▁▁▁
isValidHeaderValueOp(256)    336.57 ns/iter 340.00 ns 376.96 ns █▅▃▂▂▂▁▁▁▁▁
isValidHeaderValueOp(512)    560.46 ns/iter 569.84 ns 615.56 ns ██▄▇▅▃▂▂▂▁▁

                             ┌                                            ┐
   isValidHeaderValue($size)                                             ⡜ 1.48 µs
  isValidHeaderValue2($size)                                            ⢰⠁
 isValidHeaderValueOp($size)                                           ⢀⠇ 
                                                                       ⡜  
                                                                      ⢰⠁  
                                                                     ⢀⠇   
                                                                     ⡜    
                                                                    ⡰⠁    
                                                                   ⡰⠁     
                                                                  ⡜      ⢀
                                                                ⢀⠎     ⢀⡴⠋
                                                              ⢀⡠⠊    ⢀⠔⠋  
                                                            ⣀⠔⠁   ⣀⡤⠖⠁    
                                                        ⣀⡠⠔⠊⣀⣀⣤⠴⠖⠋⠁       
                                                 ⣀⣀⣤⠤⠶⠶⠝⠒⠒⠉⠉⠁             
                              ⣀⣀⣀⣤⣤⣤⣤⡤⠤⠤⠤⠤⠤⠤⠤⠔⠒⠊⠉                          6.12 ns
                             └                                            ┘

@mcollina
Copy link
Member

mcollina commented Jan 8, 2025

Wow, this is a comprehensive anaylisis. Would you like to send a PR? We'd indeed need to use both approaches, one for short headers and one for long ones.

How did you come by this improvement? Did you find that header validation was a bottleneck for a specific case in Undici? Does this improvement speed up our benchmarks?

@ronag
Copy link
Member

ronag commented Jan 8, 2025

I would prefer to just use regular expressions. Seems like a good balance between performance and maintainability.

@PandaWorker
Copy link
Author

@mcollina , In applications where a large number of network requests are required, it is important that they are as productive as possible, not only without headers, but also with headers. I measured queries without headers and with headers in profiling mode.

Also, such services often have the same requests, and it would be good to do something like a PreparedRequest that will create a structure that does not require further validation of headers and transformation of the request body.

Here are the functions that need to be reviewed for optimization:
*processHeader /undici/lib/core/request.js:322:24
*Request /undici/lib/core/request.js:31:15
*parseHeaders /undici/lib/core/util.js:408:23
RegExp: [^\t\x20-\x7e\x80-\xff]
*resumeH1 /undici/lib/dispatcher/client-h1.js:958:19

// @ts-nocheck
import { Agent, request } from 'undici';

const dispatcher = new Agent({ connections: 100 });

const header = new Headers();
header.set(`x-authorization-user-id-1`, `a`.repeat(10 * 20 * 25));

const headers = new Headers();
for (let index = 0; index < 20; index++) {
	headers.set(`x-authorization-user-id-${index}`, `a`.repeat(10));
}

const optionsWithHeaders = {
	method: 'GET',
	dispatcher,
	headers: headers,
};

const optionsWithOneHeader = {
	method: 'GET',
	dispatcher,
	headers: header,
};

const optionsWithoutHeaders = {
	method: 'GET',
	dispatcher,
};

let rpc = 0;
const rpcInterval = setInterval(() => {
	const curr = rpc;
	rpc = 0;

	curr > 0 && console.log(`rps:`, curr);
}, 1000);

async function worker(options) {
	const resp = await request('http://localhost:8000', options);
	await resp.body.dump();
	rpc += 1;
}

async function main() {
	{
		const start = performance.now();
		const tasks = Array.from({ length: 1e6 }, () => worker(optionsWithOneHeader));
		await Promise.all(tasks);
		const end = performance.now();

		console.log(`with one header:`, (end - start).toFixed(2)); // with one header: 23557.95
	}

	{
		const start = performance.now();
		const tasks = Array.from({ length: 1e6 }, () => worker(optionsWithHeaders));
		await Promise.all(tasks);
		const end = performance.now();

		console.log(`with Headers:`, (end-start).toFixed(2)); // with Headers: 20957.07
	}

	{
		const start = performance.now();
		const tasks = Array.from({ length: 1e6 }, () => worker(optionsWithoutHeaders));
		await Promise.all(tasks);
		const end = performance.now();

		console.log(`without Headers:`, (end-start).toFixed(2)); // without Headers: 17486.71
	}
}

main().then(() => clearInterval(rpcInterval));

one header (5000 len)

 [JavaScript]:
   ticks  total  nonlib   name
   2807   14.5%   14.5%  RegExp: [^\t\x20-\x7e\x80-\xff]
    277    1.4%    1.4%  JS: *wasm-function[20]
    269    1.4%    1.4%  Builtin: KeyedLoadIC_Megamorphic
    195    1.0%    1.0%  JS: *parseHeaders /node_modules/.pnpm/[email protected]/node_modules/undici/lib/core/util.js:408:23
    158    0.8%    0.8%  Builtin: LoadIC
    133    0.7%    0.7%  JS: *resumeH1 /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:958:19
    133    0.7%    0.7%  JS: *_resume /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client.js:533:18
    125    0.6%    0.6%  Builtin: CreateTypedArray
    105    0.5%    0.5%  Builtin: RecordWriteSaveFP
    101    0.5%    0.5%  JS: *processTicksAndRejections node:internal/process/task_queues:72:35
    100    0.5%    0.5%  JS: *Request /node_modules/.pnpm/[email protected]/node_modules/undici/lib/core/request.js:31:15
     91    0.5%    0.5%  JS: *<anonymous> /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/pool.js:75:20
     81    0.4%    0.4%  Builtin: RunMicrotasks
     76    0.4%    0.4%  Builtin: KeyedStoreIC_Megamorphic
     62    0.3%    0.3%  JS: *Readable.read node:internal/streams/readable:647:35
     61    0.3%    0.3%  JS: *nextTick node:internal/process/task_queues:113:18
     60    0.3%    0.3%  JS: *<anonymous> /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client.js:279:15
     59    0.3%    0.3%  JS: *dump /node_modules/.pnpm/[email protected]/node_modules/undici/lib/api/readable.js:258:14
     57    0.3%    0.3%  JS: *Readable.on node:internal/streams/readable:1127:33
     56    0.3%    0.3%  JS: *wasm_on_header_value /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:127:29
     54    0.3%    0.3%  JS: *processHeader /node_modules/.pnpm/[email protected]/node_modules/undici/lib/core/request.js:322:24
     52    0.3%    0.3%  Builtin: LoadIC_Megamorphic
     51    0.3%    0.3%  JS: *writeH1 /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:998:18
     44    0.2%    0.2%  RegExp: [^\u0021-\u00ff]
     43    0.2%    0.2%  JS: *onHeaders /node_modules/.pnpm/[email protected]/node_modules/undici/lib/core/request.js:236:13
     41    0.2%    0.2%  JS: *worker file:///http/bench/1.ts:1:711
     38    0.2%    0.2%  JS: *onDrain /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/pool-base.js:31:39

20 headers with 10 len

[JavaScript]:
   ticks  total  nonlib   name
    915    4.9%    8.0%  JS: *processHeader /node_modules/.pnpm/[email protected]/node_modules/undici/lib/core/request.js:322:24
    304    1.6%    2.7%  JS: *wasm-function[20]
    254    1.4%    2.2%  JS: *Request /node_modules/.pnpm/[email protected]/node_modules/undici/lib/core/request.js:31:15
    197    1.1%    1.7%  JS: *parseHeaders /node_modules/.pnpm/[email protected]/node_modules/undici/lib/core/util.js:408:23
    151    0.8%    1.3%  RegExp: [^\t\x20-\x7e\x80-\xff]
    119    0.6%    1.0%  JS: *resumeH1 /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:958:19
    110    0.6%    1.0%  JS: *<anonymous> /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/pool.js:75:20
    108    0.6%    0.9%  JS: *processTicksAndRejections node:internal/process/task_queues:72:35
    106    0.6%    0.9%  JS: *writeH1 /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:998:18
     97    0.5%    0.8%  JS: *_resume /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client.js:533:18
     89    0.5%    0.8%  JS: *wasm_on_header_value /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:127:29
     72    0.4%    0.6%  RegExp: [^\u0021-\u00ff]
     72    0.4%    0.6%  JS: *Readable.read node:internal/streams/readable:647:35
     69    0.4%    0.6%  JS: *nextTick node:internal/process/task_queues:113:18
     54    0.3%    0.5%  JS: *dump /node_modules/.pnpm/[email protected]/node_modules/undici/lib/api/readable.js:258:14
     52    0.3%    0.5%  JS: *<anonymous> /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client.js:279:15
     50    0.3%    0.4%  JS: *runInAsyncScope node:async_hooks:204:18
     48    0.3%    0.4%  JS: *wasm_on_header_field /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:116:29
     43    0.2%    0.4%  JS: *worker file:///http/bench/1.ts:1:555
     42    0.2%    0.4%  JS: *Readable.on node:internal/streams/readable:1127:33
     37    0.2%    0.3%  JS: *emitReadable node:internal/streams/readable:819:22
     37    0.2%    0.3%  JS: *Readable.push node:internal/streams/readable:387:35
     36    0.2%    0.3%  JS: *wasm_on_message_complete /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:158:33
     31    0.2%    0.3%  JS: *emit node:events:471:44
     30    0.2%    0.3%  JS: *Readable node:internal/streams/readable:320:18
     29    0.2%    0.3%  JS: wasm-to-js
     29    0.2%    0.3%  JS: *onHeadersComplete /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:525:21
     26    0.1%    0.2%  JS: *wasm_on_body /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:149:21
     26    0.1%    0.2%  JS: *innerOk node:internal/assert/utils:259:17
     25    0.1%    0.2%  JS: *onDrain /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/pool-base.js:31:39

without headers

[JavaScript]:
   ticks  total  nonlib   name
    330    2.4%    2.4%  JS: *wasm-function[20]
    239    1.7%    1.7%  Builtin: KeyedLoadIC_Megamorphic
    188    1.4%    1.4%  JS: *parseHeaders /node_modules/.pnpm/[email protected]/node_modules/undici/lib/core/util.js:408:23
    136    1.0%    1.0%  Builtin: CreateTypedArray
    128    0.9%    0.9%  JS: *resumeH1 /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:958:19
    109    0.8%    0.8%  JS: *Request /node_modules/.pnpm/[email protected]/node_modules/undici/lib/core/request.js:31:15
    108    0.8%    0.8%  Builtin: RecordWriteSaveFP
    107    0.8%    0.8%  Builtin: LoadIC
     89    0.6%    0.6%  JS: *_resume /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client.js:533:18
     85    0.6%    0.6%  JS: *<anonymous> /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/pool.js:75:20
     84    0.6%    0.6%  JS: *processTicksAndRejections node:internal/process/task_queues:72:35
     81    0.6%    0.6%  Builtin: KeyedStoreIC_Megamorphic
     80    0.6%    0.6%  JS: *Readable.read node:internal/streams/readable:647:35
     72    0.5%    0.5%  JS: *wasm_on_header_value /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:127:29
     70    0.5%    0.5%  JS: *nextTick node:internal/process/task_queues:113:18
     69    0.5%    0.5%  JS: *dump /node_modules/.pnpm/[email protected]/node_modules/undici/lib/api/readable.js:258:14
     59    0.4%    0.4%  Builtin: LoadIC_Megamorphic
     58    0.4%    0.4%  RegExp: [^\u0021-\u00ff]
     56    0.4%    0.4%  Builtin: RunMicrotasks
     53    0.4%    0.4%  JS: *<anonymous> /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client.js:279:15
     49    0.4%    0.4%  JS: *writeH1 /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:998:18
     46    0.3%    0.3%  JS: *runInAsyncScope node:async_hooks:204:18
     45    0.3%    0.3%  JS: *Readable node:internal/streams/readable:320:18
     44    0.3%    0.3%  Builtin: CallApiCallbackOptimizedNoProfiling
     37    0.3%    0.3%  JS: *wasm_on_header_field /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/client-h1.js:116:29
     37    0.3%    0.3%  Builtin: JSConstructStubGeneric
     37    0.3%    0.3%  Builtin: FulfillPromise
     35    0.3%    0.3%  JS: *request /node_modules/.pnpm/[email protected]/node_modules/undici/lib/api/api-request.js:176:18
     34    0.2%    0.2%  JS: *onDrain /node_modules/.pnpm/[email protected]/node_modules/undici/lib/dispatcher/pool-base.js:31:39

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants