Skip to content

Instantly share code, notes, and snippets.

@billywhizz
Last active February 16, 2026 05:05
Show Gist options
  • Select an option

  • Save billywhizz/67fa973b70a20ed974834e9894e82a04 to your computer and use it in GitHub Desktop.

Select an option

Save billywhizz/67fa973b70a20ed974834e9894e82a04 to your computer and use it in GitHub Desktop.

issue summary

  • setImmediate in node.js has a lot of overhead on macos compared to linux. attention was drawn to this in this tweet from jarred sumner.
  • in the referenced benchmark, node on macos was only able to achieve 80k ticks per second of the event loop versus millions per second for Bun & Deno on macos
  • on linux, difference between node, Bun and Deno are much less significant
  • the change implemented in Bun for MacOS uses the KEVENT_FLAG_IMMEDIATE flag to create a fast return from the syscall when there is no pending io.
  • the flag in question is only available when using the kevent64 syscall on macos
  • libuv uses the kevent syscall, which is common across bsd based platforms.
  • i built a version of node with patches to libuv to use kevent64 and to set the flag for the fast path
  • i haven't verified the logic for this fast path, but it is enough to run this benchmark and run the node.js repl
  • i haven't run any test suites for libuv or node.js to check for breakage
  • this would ideally land in libuv but would need to be gated behind some feature detection for macos versions that support kevent64 syscall
  • i am not sure how many real world use cases this will affect - you would need to be making heavy use of setImmediate to see the improvements noted on the original tweet and below
  • the results are pretty impressive - ~80k event loop ticks per second versus ~2m - that's a 25x improvement for this particular microbenchmark
  • for fun, i also implemented this "trick" in my lo runtime. the results are interesting. with the event loop exposed to JS, we see a significant improvement in throughput - ~5.4m ticks per second versus ~3m for the patched/improved version of Bun (not released yet).
  • that's 1.75x better throughput when you expose the event loop to JS. this makes sense as the time per tick is ~180 nanoseconds. A v8 fastcall to kevent64 only adds ~5 nanos overhead which is roughly 3% overhead on an optimized C implementation. while for Bun, Node.js and Deno the event loop is managed in C++/Rust/Zig land and there is a much bigger cost to calling back from C++ to JS on every tick of the event loop.
  • there is likely also other overhead in the Bun/Node/Deno/libuv event loop implementations on top of the rather simplistic approach lo currently takes. so, not exactly a fair comparison but interesting all the same.
  • you can repro the results by following the instructions below.
  • the changes to libuv are also available here as a patch

results

clone the lo repo

git clone git@github.com:just-js/lo.git
cd lo

build on mac silicon

make ARCH=arm64 lo

build on linux/x64

make lo

run the bench

mv ../loop-test.js ./
./lo loop-test.js
bun loop-test.js
node loop-test.js
deno -A loop-test.js

build node on macos

git clone --depth 1 --single-branch --branch feat-libuv-mac-event64 git@github.com:billywhizz/node.git
cd node
cp ../build-node.sh
./build-node.sh

results

$ node looptest.js 
node       setImmediate                0 time      999 rate        81680 rate/core       635177 ns/iter     12243.02 rss     56262656 usr   4.99 sys   7.87 tot  12.86
node       setImmediate                0 time      999 rate        82129 rate/core       692510 ns/iter     12176.08 rss     56328192 usr   4.27 sys   7.59 tot  11.86
node       setImmediate                0 time     1000 rate        82090 rate/core       682610 ns/iter     12181.86 rss     56524800 usr   4.23 sys   7.80 tot  12.03
node       setImmediate                0 time      999 rate        82087 rate/core       682918 ns/iter     12182.22 rss     56524800 usr   4.18 sys   7.84 tot  12.02
node       setImmediate                0 time     1000 rate        81913 rate/core       675111 ns/iter     12208.18 rss     56524800 usr   4.22 sys   7.91 tot  12.13
node       setImmediate                0 time      999 rate        82108 rate/core       675236 ns/iter     12179.13 rss     56557568 usr   4.28 sys   7.88 tot  12.16
node       setImmediate                0 time     1000 rate        81994 rate/core       674694 ns/iter     12196.09 rss     57212928 usr   4.40 sys   7.76 tot  12.15
node       setImmediate                0 time      999 rate        82226 rate/core       685684 ns/iter     12161.64 rss     57409536 usr   4.31 sys   7.68 tot  11.99
node       setImmediate                0 time      999 rate        82004 rate/core       670164 ns/iter     12194.55 rss     57507840 usr   4.31 sys   7.93 tot  12.24
node       setImmediate                0 time     1000 rate        82221 rate/core       691759 ns/iter     12162.43 rss     57802752 usr   4.33 sys   7.55 tot  11.89

$ deno -A looptest.js
deno       setImmediate                0 time     1000 rate      1573811 rate/core      2185848 ns/iter       635.40 rss     62930944 usr  70.00 sys   2.00 tot  72.00
deno       setImmediate                0 time      999 rate      1587635 rate/core      2236106 ns/iter       629.86 rss     63766528 usr  69.00 sys   2.00 tot  71.00
deno       setImmediate                0 time     1000 rate      1576824 rate/core      2190032 ns/iter       634.18 rss     65748992 usr  69.00 sys   3.00 tot  72.00
deno       setImmediate                0 time      999 rate      1574990 rate/core      2187486 ns/iter       634.92 rss     65748992 usr  70.00 sys   2.00 tot  72.00
deno       setImmediate                0 time     1000 rate      1588744 rate/core      2237668 ns/iter       629.42 rss     65748992 usr  69.00 sys   2.00 tot  71.00
deno       setImmediate                0 time     1000 rate      1591856 rate/core      2180625 ns/iter       628.19 rss     65748992 usr  70.00 sys   3.00 tot  73.00
deno       setImmediate                0 time     1000 rate      1582943 rate/core      2229497 ns/iter       631.73 rss     65781760 usr  69.00 sys   2.00 tot  71.00
deno       setImmediate                0 time      999 rate      1581955 rate/core      2197159 ns/iter       632.12 rss     65863680 usr  70.00 sys   2.00 tot  72.00
deno       setImmediate                0 time      999 rate      1576951 rate/core      2190210 ns/iter       634.13 rss     65961984 usr  69.00 sys   3.00 tot  72.00
deno       setImmediate                0 time      999 rate      1585739 rate/core      2233436 ns/iter       630.62 rss     66158592 usr  69.00 sys   2.00 tot  71.00

$ bun looptest.js
bun        setImmediate                0 time     1000 rate      1380475 rate/core      1364487 ns/iter       724.38 rss     47693824 usr  50.79 sys  50.38 tot 101.17
bun        setImmediate                0 time     1000 rate      1406453 rate/core      1403626 ns/iter       711.00 rss     50249728 usr  49.88 sys  50.32 tot 100.20
bun        setImmediate                0 time     1000 rate      1404861 rate/core      1402909 ns/iter       711.81 rss     50266112 usr  50.04 sys  50.10 tot 100.14
bun        setImmediate                0 time     1000 rate      1406440 rate/core      1403612 ns/iter       711.01 rss     50266112 usr  50.10 sys  50.10 tot 100.20
bun        setImmediate                0 time     1000 rate      1412762 rate/core      1409361 ns/iter       707.83 rss     50266112 usr  50.09 sys  50.15 tot 100.24
bun        setImmediate                0 time     1000 rate      1413602 rate/core      1411054 ns/iter       707.41 rss     50266112 usr  50.12 sys  50.06 tot 100.18
bun        setImmediate                0 time     1000 rate      1413208 rate/core      1410489 ns/iter       707.61 rss     50266112 usr  50.14 sys  50.06 tot 100.19
bun        setImmediate                0 time     1000 rate      1406583 rate/core      1404246 ns/iter       710.94 rss     50266112 usr  50.10 sys  50.07 tot 100.17
bun        setImmediate                0 time     1000 rate      1410313 rate/core      1407054 ns/iter       709.06 rss     54607872 usr  50.17 sys  50.06 tot 100.23
bun        setImmediate                0 time     1000 rate      1411812 rate/core      1410117 ns/iter       708.30 rss     54607872 usr  50.12 sys  50.00 tot 100.12

$ ../../bun/build/release/bun looptest.js 
bun        setImmediate                0 time     1000 rate      2985652 rate/core      2953065 ns/iter       334.93 rss     50298880 usr  72.36 sys  28.75 tot 101.10
bun        setImmediate                0 time     1000 rate      3017227 rate/core      3010379 ns/iter       331.43 rss     50462720 usr  71.75 sys  28.48 tot 100.23
bun        setImmediate                0 time     1000 rate      3032942 rate/core      3024470 ns/iter       329.71 rss     50462720 usr  71.71 sys  28.57 tot 100.28
bun        setImmediate                0 time     1000 rate      3030591 rate/core      3023305 ns/iter       329.96 rss     50462720 usr  71.65 sys  28.59 tot 100.24
bun        setImmediate                0 time     1000 rate      3024676 rate/core      3018955 ns/iter       330.61 rss     59244544 usr  71.63 sys  28.56 tot 100.19
bun        setImmediate                0 time     1000 rate      3031503 rate/core      3022984 ns/iter       329.86 rss     59244544 usr  71.64 sys  28.64 tot 100.28
bun        setImmediate                0 time     1001 rate      3019978 rate/core      3013308 ns/iter       331.12 rss     59244544 usr  71.75 sys  28.47 tot 100.22
bun        setImmediate                0 time     1000 rate      3023912 rate/core      3015604 ns/iter       330.69 rss     59244544 usr  71.80 sys  28.47 tot 100.28
bun        setImmediate                0 time     1000 rate      3019590 rate/core      3008878 ns/iter       331.17 rss     59244544 usr  71.92 sys  28.44 tot 100.36
bun        setImmediate                0 time     1001 rate      3016213 rate/core      3007895 ns/iter       331.54 rss     59244544 usr  71.77 sys  28.51 tot 100.28

$ out/Release/node looptest.js
node       setImmediate                0 time      998 rate      2004909 rate/core      1988882 ns/iter       498.77 rss     55902208 usr  81.84 sys  18.96 tot 100.81
node       setImmediate                0 time      999 rate      1991330 rate/core      1989102 ns/iter       502.17 rss     56016896 usr  81.30 sys  18.81 tot 100.11
node       setImmediate                0 time      999 rate      1991522 rate/core      1988931 ns/iter       502.12 rss     56459264 usr  81.24 sys  18.89 tot 100.13
node       setImmediate                0 time      999 rate      1961789 rate/core      1959355 ns/iter       509.73 rss     56475648 usr  81.50 sys  18.62 tot 100.12
node       setImmediate                0 time     1000 rate      1974841 rate/core      1972340 ns/iter       506.36 rss     56475648 usr  81.38 sys  18.74 tot 100.13
node       setImmediate                0 time      999 rate      1975403 rate/core      1972695 ns/iter       506.22 rss     56508416 usr  81.37 sys  18.77 tot 100.14
node       setImmediate                0 time     1000 rate      1988128 rate/core      1985321 ns/iter       502.98 rss     56688640 usr  81.27 sys  18.87 tot 100.14
node       setImmediate                0 time     1000 rate      2011094 rate/core      2008531 ns/iter       497.24 rss     60030976 usr  81.01 sys  19.11 tot 100.13
node       setImmediate                0 time     1000 rate      2024789 rate/core      2023096 ns/iter       493.87 rss     60096512 usr  81.06 sys  19.02 tot 100.08
node       setImmediate                0 time      999 rate      2023252 rate/core      2021279 ns/iter       494.25 rss     60211200 usr  81.06 sys  19.04 tot 100.10

$ lo looptest.js
lo         setImmediate                0 time     1010 rate      5357262 rate/core      5304740 ns/iter       186.66 rss     29294592 usr  53.47 sys  47.52 tot 100.99
lo         setImmediate                0 time      999 rate      5403884 rate/core      5403884 ns/iter       185.05 rss     31457280 usr  53.00 sys  47.00 tot 100.00
lo         setImmediate                0 time      999 rate      5413724 rate/core      5413724 ns/iter       184.71 rss     31997952 usr  54.00 sys  46.00 tot 100.00
lo         setImmediate                0 time     1000 rate      5388907 rate/core      5388907 ns/iter       185.56 rss     32522240 usr  53.00 sys  47.00 tot 100.00
lo         setImmediate                0 time      999 rate      5397327 rate/core      5397327 ns/iter       185.27 rss     32948224 usr  53.00 sys  47.00 tot 100.00
lo         setImmediate                0 time     1000 rate      5396567 rate/core      5396567 ns/iter       185.30 rss     33439744 usr  53.00 sys  47.00 tot 100.00
lo         setImmediate                0 time      997 rate      5408435 rate/core      5354887 ns/iter       184.89 rss     33832960 usr  54.00 sys  47.00 tot 101.00
lo         setImmediate                0 time     1002 rate      5410258 rate/core      5410258 ns/iter       184.83 rss     34209792 usr  53.00 sys  47.00 tot 100.00
lo         setImmediate                0 time      998 rate      5404572 rate/core      5404572 ns/iter       185.02 rss     34717696 usr  53.00 sys  47.00 tot 100.00
lo         setImmediate                0 time      997 rate      5402289 rate/core      5348801 ns/iter       185.10 rss     35192832 usr  54.00 sys  47.00 tot 101.00
/*
this runs on bun/lo/deno/node on macos and linux
need to test on windows
*/
function is_a_tty(fd = 1) {
if (globalThis.Deno) return Deno.isatty(fd);
if (globalThis.lo) return lo.core.isatty(fd);
if (fd === 1) return process.stdout.isTTY;
if (fd === 2) return process.stderr.isTTY;
return process.stdin.isTTY;
}
const isatty = is_a_tty();
const AD = isatty ? "\u001b[0m" : ""; // ANSI Default
const A0 = isatty ? "\u001b[30m" : ""; // ANSI Black
const AR = isatty ? "\u001b[31m" : ""; // ANSI Red
const AG = isatty ? "\u001b[32m" : ""; // ANSI Green
const AY = isatty ? "\u001b[33m" : ""; // ANSI Yellow
const AB = isatty ? "\u001b[34m" : ""; // ANSI Blue
const AM = isatty ? "\u001b[35m" : ""; // ANSI Magenta
const AC = isatty ? "\u001b[36m" : ""; // ANSI Cyan
const AW = isatty ? "\u001b[37m" : ""; // ANSI White
const colors = { AD, AG, AY, AM, AD, AR, AB, AC, AW, A0 };
const decoder = new TextDecoder();
const encoder = new TextEncoder();
class Stats {
recv = 0;
send = 0;
conn = 0;
rps = 0;
log() {
const { send, recv, conn, rps } = this;
const [usr, sys] = cputime();
const rps_per_core = Math.floor(rps / ((usr + sys) / 100))
console.log(
`${AC}send${AD} ${to_size_string(send)} ${AC}recv${AD} ${to_size_string(recv)} ${AC}rps${AD} ${rps.toString().padStart(10, ' ')} ${AM}rps/core${AD} ${rps_per_core.toString().padStart(10, ' ')} ${AC}rss${AD} ${mem().toString().padStart(12, ' ')} ${AC}con${AD} ${conn} ${AY}usr${AD} ${usr.toString().padStart(3, " ")} ${AY}sys${AD} ${sys.toString().padStart(3, " ")} ${AY}tot${AD} ${(usr + sys).toString().padStart(3, " ")}`,
);
this.send = this.recv = this.rps = 0;
}
get runtime() {
return lo.hrtime() - lo.start;
}
}
function pad(v, size, precision = 0) {
return v.toFixed(precision).padStart(size, " ");
}
function memory_usage(buf) {
return Math.floor(Number(decoder.decode(buf).split(" ")[23]) * 4096);
}
let lastusr = 0;
let lastsys = 0;
let last_time = Date.now();
function cpu_usage(bytes) {
const str = decoder.decode(bytes);
const parts = str.split(" ");``
const elapsed = Date.now() - last_time;
const usr = Number(parts[13]);
const sys = Number(parts[14]);
const res = [(((usr - lastusr) * 10) / elapsed) * 100, (((sys - lastsys) * 10) / elapsed) * 100].map(v => Math.floor(v))
lastusr = usr;
lastsys = sys;
last_time = Date.now();
return res;
}
async function wrap_mem_usage() {
if (globalThis.Deno) {
if (Deno.build.os === "linux") {
const mem = () => memory_usage(Deno.readFileSync("/proc/self/stat"));
const cputime = () => cpu_usage(Deno.readFileSync("/proc/self/stat"));
cputime();
return { mem, cputime };
}
const mem = () => Deno.memoryUsage().rss;
const _SC_CLK_TCK = 3;
const api = Deno.dlopen("libc.dylib", {
sysconf: { parameters: ["i32"], result: "i32" },
times: { parameters: ["buffer"], result: "i32" },
}).symbols;
const { sysconf, times } = api;
const clock_ticks_per_second = sysconf(_SC_CLK_TCK);
const last = new Int32Array(5);
const current = new Int32Array(6);
current[5] = clock_ticks_per_second;
const time32 = new Uint32Array(9);
const _cputime = () => {
time32[4] = times(time32);
for (let i = 0; i < 5; i++) {
current[i] = time32[i] - last[i];
last[i] = time32[i];
}
return current;
};
_cputime();
return {
mem,
cputime: () => {
const time = _cputime();
const usr = time[0] / (time[4] / time[5]);
const sys = time[2] / (time[4] / time[5]);
return [usr, sys];
},
};
}
if (globalThis.process && !globalThis.lo) {
const os = await import("os");
if (os.platform() === "linux") {
const fs = await import("fs");
const mem = () => memory_usage(fs.readFileSync("/proc/self/stat"));
const cputime = () => cpu_usage(fs.readFileSync("/proc/self/stat"));
cputime();
return { mem, cputime };
}
let prev = process.cpuUsage();
let since = Date.now();
return {
mem: () => process.memoryUsage().rss,
cputime: () => {
const now = Date.now();
const elapsed_ms = now - since;
const cpu = process.cpuUsage();
const user = cpu.user - prev.user;
const system = cpu.system - prev.system;
prev = cpu;
const usr = (user / 1000 / elapsed_ms) * 100;
const sys = (system / 1000 / elapsed_ms) * 100;
since = Date.now();
return [usr, sys];
},
};
}
if (globalThis.lo) {
const proc = await import("lib/proc.js");
proc.cputime();
const cputime = () => {
const time = proc.cputime();
const usr = time[0] / (time[4] / time[5]);
const sys = time[2] / (time[4] / time[5]);
return [usr, sys];
};
return { mem: proc.mem, cputime };
}
}
function to_size_string(bytes) {
if (bytes < 1000) {
return `${bytes.toFixed(2).padStart(8, " ")} ${AY} Bps${AD}`;
} else if (bytes < 1000 * 1000) {
return `${(Math.floor((bytes / 1000) * 100) / 100).toFixed(2).padStart(8, " ")} ${AY}KBps${AD}`;
} else if (bytes < 1000 * 1000 * 1000) {
return `${(Math.floor((bytes / (1000 * 1000)) * 100) / 100).toFixed(2).padStart(8, " ")} ${AY}MBps${AD}`;
}
return `${(Math.floor((bytes / (1000 * 1000 * 1000)) * 100) / 100).toFixed(2).padStart(8, " ")} ${AY}GBps${AD}`;
}
function formatNanos(nanos) {
if (nanos >= 1000000000) return `${AY}sec/iter${AD} ${pad(nanos / 1000000000, 10, 2)}`;
if (nanos >= 1000000) return `${AY}ms/iter${AD} ${pad(nanos / 1000000, 10, 2)}`;
if (nanos >= 1000) return `${AY}μs/iter${AD} ${pad(nanos / 1000, 10, 2)}`;
return `${AY}ns/iter${AD} ${pad(nanos, 10, 2)}`;
}
function bench(name, fn, count, after = noop) {
const start = performance.now();
for (let i = 0; i < count; i++) fn();
const elapsed = performance.now() - start;
const rate = Math.floor(count / (elapsed / 1000));
const nanos = 1000000000 / rate;
const rss = mem();
console.log(
`${name.slice(0, 32).padEnd(17, " ")} ${pad(Math.floor(elapsed), 6)} ms ${AG}rate${AD} ${pad(rate, 10)} ${formatNanos(nanos)} ${AG}rss${AD} ${rss}`,
);
after();
return { name, count, elapsed, rate, nanos, rss, runtime };
}
async function benchAsync(name, fn, count, after = noop) {
const start = performance.now();
for (let i = 0; i < count; i++) await fn();
const elapsed = performance.now() - start;
const rate = Math.floor((count / (elapsed / 1000)) * 100) / 100;
const nanos = 1000000000 / rate;
const rss = mem();
console.log(
`${name.slice(0, 32).padEnd(17, " ")} ${pad(Math.floor(elapsed), 6)} ms ${AG}rate${AD} ${pad(rate, 10)} ${formatNanos(nanos)} ${AG}rss${AD} ${rss}`,
);
after();
return { name, count, elapsed, rate, nanos, rss, runtime };
}
const runAsync = async (name, fn, count, repeat = 10, after = () => {}) => {
const runs = [];
for (let i = 0; i < repeat; i++) {
runs.push(await benchAsync(name, fn, count, after));
}
return runs;
};
const run = (name, fn, count, repeat = 10, after = () => {}) => {
const runs = [];
for (let i = 0; i < repeat; i++) {
runs.push(bench(name, fn, count, after));
}
return runs;
};
function arrayEquals(a, b) {
return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((val, index) => val === b[index]);
}
class Bench {
#start = 0;
#end = 0;
#name = "bench";
#display = true;
#name_width = 20;
#runtime = "";
constructor(display = true) {
this.#display = display;
this.#runtime = `${runtime.name}_${runtime.version}`
}
set name_width(len) {
this.#name_width = len;
}
start(name = "bench") {
this.#name = name.slice(0, 32).padEnd(32, " ");
this.#start = performance.now();
}
end(count = 0, size = 0) {
this.#end = performance.now();
const elapsed = this.#end - this.#start;
const nanos = Math.floor(elapsed * 1000000);
const seconds = nanos / 1000000000;
let rate = count / seconds;
const rss = mem();
const [usr, sys] = cputime();
const total = usr + sys;
const rate_pc_f = rate / (total / 100);
const rate_pc = rate_pc_f > 100 ? Math.ceil(rate_pc_f) : Math.ceil(rate_pc_f * 100) / 100;
const ns_iter = Math.floor((nanos / count) * 100) / 100;
rate = rate > 100 ? Math.ceil(rate) : Math.ceil(rate * 100) / 100;
if (this.#display) {
if (size) {
console.log(
`${AC}${this.#runtime.padEnd(16, " ")}${AD} ${AM}${this.#name.trim().padEnd(this.#name_width, " ")}${AD} ${AY}time${AD} ${Math.floor(elapsed).toString().padStart(8, " ")} ${AY}rate${AD} ${rate.toString().padStart(12, " ")} ${AM}rate/core${AD} ${rate_pc.toString().padStart(12, " ")} ${AG}ns/iter${AD} ${ns_iter.toFixed(2).padStart(12, " ")} ${AG}rss${AD} ${rss.toString().padStart(12, " ")} ${AG}usr${AD} ${usr.toFixed(2).padStart(6, " ")} ${AR}sys${AD} ${sys.toFixed(2).padStart(6, " ")} ${AY}tot${AD} ${total.toFixed(2).padStart(6, " ")} ${AG}thru${AD} ${to_size_string(rate_pc * size).padStart(12, " ")}`,
);
} else {
console.log(
`${AC}${this.#runtime.padEnd(16, " ")}${AD} ${AM}${this.#name.trim().padEnd(this.#name_width, " ")}${AD} ${AY}time${AD} ${Math.floor(elapsed).toString().padStart(8, " ")} ${AY}rate${AD} ${rate.toString().padStart(12, " ")} ${AM}rate/core${AD} ${rate_pc.toString().padStart(12, " ")} ${AG}ns/iter${AD} ${ns_iter.toFixed(2).padStart(12, " ")} ${AG}rss${AD} ${rss.toString().padStart(12, " ")} ${AG}usr${AD} ${usr.toFixed(2).padStart(6, " ")} ${AR}sys${AD} ${sys.toFixed(2).padStart(6, " ")} ${AY}tot${AD} ${total.toFixed(2).padStart(6, " ")}`,
);
}
}
return { name: this.#name.trim(), count, elapsed, rate, nanos, rss, runtime, usr, sys, rate_pc, ns_iter, seconds };
}
}
const runtime = { name: "", version: "" };
if (globalThis.Deno) {
globalThis.args = Deno.args;
runtime.name = "deno";
runtime.version = Deno.version.deno;
runtime.v8 = Deno.version.v8;
globalThis.readFileAsText = async fn => decoder.decode(Deno.readFileSync(fn));
globalThis.readFileAsBytes = async fn => Deno.readFileSync(fn);
globalThis.writeFileAsText = async (fn, str) => Deno.writeFileSync(fn, encoder.encode(str));
globalThis.writeFileAsBytes = async (fn, u8) => Deno.writeFileSync(fn, u8);
const fs = await import("node:fs");
globalThis.openSync = fs.openSync;
globalThis.readSync = fs.readSync;
globalThis.closeSync = fs.closeSync;
globalThis.readFileSync = fs.readFileSync;
globalThis.process = await import('node:process')
const { Buffer } = await import('node:buffer')
globalThis.Buffer = Buffer
} else if (globalThis.lo) {
globalThis.performance = { now: () => lo.hrtime() / 1000000 };
globalThis.assert = lo.assert;
globalThis.args = lo.args.slice(2);
runtime.name = "lo";
runtime.version = lo.version.lo;
runtime.v8 = lo.version.v8;
const { readFile, writeFile } = lo.core;
const { close, open, read, O_RDONLY } = lo.core;
globalThis.readFileAsText = async fn => decoder.decode(readFile(fn));
globalThis.readFileAsBytes = async fn => readFile(fn);
globalThis.writeFileAsText = async (fn, str) => writeFile(fn, encoder.encode(str));
globalThis.writeFileAsBytes = async (fn, u8) => writeFile(fn, u8);
globalThis.openSync = file_name => open(file_name, O_RDONLY);
globalThis.readSync = (fd, buf) => read(fd, buf, buf.length);
globalThis.closeSync = fd => close(fd);
globalThis.readFileSync = readFile;
globalThis.process = { argv: lo.args }
} else if (globalThis.Bun) {
globalThis.args = Bun.argv.slice(2);
runtime.name = "bun";
runtime.version = Bun.version;
globalThis.readFileAsText = async fn => await Bun.file(fn).text();
globalThis.readFileAsBytes = async fn => await Bun.file(fn).bytes();
globalThis.writeFileAsText = async (fn, str) => Bun.write(fn, str);
globalThis.writeFileAsBytes = async (fn, u8) => Bun.write(fn, u8);
const fs = await import("node:fs");
globalThis.openSync = fs.openSync;
globalThis.readSync = fs.readSync;
globalThis.closeSync = fs.closeSync;
globalThis.readFileSync = fs.readFileSync;
} else if (globalThis.process) {
globalThis.args = process.argv.slice(2);
runtime.name = "node";
runtime.version = process.version;
runtime.v8 = process.versions.v8;
const fs = await import("fs");
globalThis.readFileAsText = async fn => decoder.decode(fs.readFileSync(fn));
globalThis.readFileAsBytes = async fn => fs.readFileSync(fn);
globalThis.writeFileAsText = async (fn, str) => fs.writeFileSync(fn, encoder.encode(str));
globalThis.writeFileAsBytes = async (fn, u8) => fs.writeFileSync(fn, u8);
globalThis.openSync = fs.openSync;
globalThis.readSync = fs.readSync;
globalThis.closeSync = fs.closeSync;
globalThis.readFileSync = fs.readFileSync;
}
globalThis.colors = colors;
globalThis.arrayEquals = arrayEquals;
const noop = () => {};
const { mem, cputime } = await wrap_mem_usage();
if (!globalThis.assert) {
function fix_stack (err) {
err.stack = err.stack.split('\n')
.filter(line => !line.match(/\s+at assert \(main\.js.+/))
.join('\n')
}
function assert (condition, message, ErrorType = Error) {
if (!condition) {
if (message && message.constructor.name === 'Function') {
const err = new ErrorType(message(condition))
fix_stack(err)
throw(err)
}
const err = new ErrorType(message || "Assertion failed")
fix_stack(err)
throw(err)
}
return condition
}
globalThis.assert = assert;
}
const measure = {
start: (name = 'bench') => {
cputime();
let start = performance.now();
function end (count) {
const now = performance.now()
const nanos = Math.floor((now - start) * 1000000);
const elapsed = nanos / 1000000;
const seconds = nanos / 1000000000;
let rate = count / seconds;
const rss = mem();
const [usr, sys] = cputime();
const total = usr + sys;
const rate_pc_f = rate / (total / 100);
const rate_pc = rate_pc_f > 100 ? Math.ceil(rate_pc_f) : Math.ceil(rate_pc_f * 100) / 100;
const ns_iter = Math.floor((nanos / count) * 100) / 100;
rate = rate > 100 ? Math.ceil(rate) : Math.ceil(rate * 100) / 100;
start = now
return { rss, usr, sys, total, elapsed, rate, rate_pc, ns_iter }
}
function log (count, size = 0) {
const { rss, usr, sys, total, elapsed, rate, rate_pc, ns_iter } = end(count)
if (size) {
console.log(`${AG}${runtime.name.padEnd(10, ' ')}${AD} ${AM}${name.slice(0, 24).padEnd(20, ' ')}${AD} ${AY}${size.toString().padStart(8, ' ')}${AD} ${AY}time${AD} ${Math.floor(elapsed).toString().padStart(8, " ")} ${AY}rate${AD} ${rate.toString().padStart(12, " ")} ${AM}rate/core${AD} ${rate_pc.toString().padStart(12, " ")} ${AG}ns/iter${AD} ${ns_iter.toFixed(2).padStart(12, " ")} ${AG}rss${AD} ${rss.toString().padStart(12, " ")} ${AG}usr${AD} ${usr.toFixed(2).padStart(6, " ")} ${AR}sys${AD} ${sys.toFixed(2).padStart(6, " ")} ${AY}tot${AD} ${total.toFixed(2).padStart(6, " ")} ${AG}thru${AD} ${to_size_string(rate_pc * size).padStart(12, " ")}`,)
} else {
console.log(`${AG}${runtime.name.padEnd(10, ' ')}${AD} ${AM}${name.slice(0, 24).padEnd(20, ' ')}${AD} ${AY}${size.toString().padStart(8, ' ')}${AD} ${AY}time${AD} ${Math.floor(elapsed).toString().padStart(8, " ")} ${AY}rate${AD} ${rate.toString().padStart(12, " ")} ${AM}rate/core${AD} ${rate_pc.toString().padStart(12, " ")} ${AG}ns/iter${AD} ${ns_iter.toFixed(2).padStart(12, " ")} ${AG}rss${AD} ${rss.toString().padStart(12, " ")} ${AG}usr${AD} ${usr.toFixed(2).padStart(6, " ")} ${AR}sys${AD} ${sys.toFixed(2).padStart(6, " ")} ${AY}tot${AD} ${total.toFixed(2).padStart(6, " ")}`,)
}
}
return { end, log }
}
};
export {
pad,
formatNanos,
colors,
run,
runAsync,
Bench,
mem,
runtime,
to_size_string,
Stats,
cputime,
measure,
is_a_tty,
cpu_usage,
memory_usage,
};
#!/usr/bin/env bash
export CC="ccache clang"
export CXX="ccache clang++"
./configure --ninja --without-intl --without-lief --without-sqlite --without-npm --dest-cpu arm64 --without-amaro --without-node-options --without-inspector --debug-symbols --debug-symbols --without-node-code-cache --without-node-snapshot
ninja -C out/Release -t compdb cxx cc > compile_commands.json
ninja -C out/Release -j 12
import { measure } from './bench.js'
let counter = 0
function foo () {
counter += 1
setImmediate(foo)
}
setInterval(() => {
stats.log(counter)
counter = 0
}, 1000)
const stats = measure.start('setImmediate')
setImmediate(foo)
if(globalThis.lo) {
const { loop } = lo
while(loop.poll() > 0) {}
}
diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c
index 538ae787..985676f3 100644
--- a/deps/uv/src/unix/async.c
+++ b/deps/uv/src/unix/async.c
@@ -45,17 +45,17 @@ static int kqueue_evfilt_user_support = 1;
static void uv__kqueue_runtime_detection(void) {
int kq;
- struct kevent ev[2];
+ struct kevent64_s ev[2];
struct timespec timeout = {0, 0};
/* Perform the runtime detection to ensure that kqueue with
* EVFILT_USER actually works. */
kq = kqueue();
- EV_SET(ev, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
- EV_ADD | EV_CLEAR, 0, 0, 0);
- EV_SET(ev + 1, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
- 0, NOTE_TRIGGER, 0, 0);
- if (kevent(kq, ev, 2, ev, 1, &timeout) < 1 ||
+ EV_SET64(ev, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
+ EV_ADD | EV_CLEAR, 0, 0, 0, 0, 0);
+ EV_SET64(ev + 1, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
+ 0, NOTE_TRIGGER, 0, 0, 0, 0);
+ if (kevent64(kq, ev, 2, ev, 1, 0, &timeout) < 1 ||
ev[0].filter != EVFILT_USER ||
ev[0].ident != UV__KQUEUE_EVFILT_USER_IDENT ||
ev[0].flags & EV_ERROR)
@@ -228,12 +228,12 @@ static void uv__async_send(uv_loop_t* loop) {
fd = loop->async_io_watcher.fd; /* eventfd */
}
#elif UV__KQUEUE_EVFILT_USER
- struct kevent ev;
+ struct kevent64_s ev;
if (kqueue_evfilt_user_support) {
fd = loop->async_io_watcher.fd; /* magic number for EVFILT_USER */
- EV_SET(&ev, fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
- r = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL);
+ EV_SET64(&ev, fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0, 0, 0);
+ r = kevent64(loop->backend_fd, &ev, 1, NULL, 0, 0, NULL);
if (r == 0)
return;
abort();
@@ -259,7 +259,7 @@ static int uv__async_start(uv_loop_t* loop) {
int pipefd[2];
int err;
#if UV__KQUEUE_EVFILT_USER
- struct kevent ev;
+ struct kevent64_s ev;
#endif
if (loop->async_io_watcher.fd != -1)
@@ -293,8 +293,8 @@ static int uv__async_start(uv_loop_t* loop) {
* Since uv__async_send() may happen before uv__io_poll() with multi-threads,
* we can't defer this registration of EVFILT_USER event as we did for other
* events, but must perform it right away. */
- EV_SET(&ev, err, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0);
- err = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL);
+ EV_SET64(&ev, err, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0, 0, 0);
+ err = kevent64(loop->backend_fd, &ev, 1, NULL, 0, 0, NULL);
if (err < 0)
return UV__ERR(errno);
} else {
diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c
index 39b72012..237bc744 100644
--- a/deps/uv/src/unix/kqueue.c
+++ b/deps/uv/src/unix/kqueue.c
@@ -97,7 +97,7 @@ int uv__io_fork(uv_loop_t* loop) {
int uv__io_check_fd(uv_loop_t* loop, int fd) {
- struct kevent ev[2];
+ struct kevent64_s ev[2];
struct stat sb;
#ifdef __APPLE__
char path[MAXPATHLEN];
@@ -132,21 +132,21 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) {
}
#endif
- EV_SET(ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
- EV_SET(ev + 1, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
- if (kevent(loop->backend_fd, ev, 2, NULL, 0, NULL))
+ EV_SET64(ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0, 0, 0);
+ EV_SET64(ev + 1, fd, EVFILT_READ, EV_DELETE, 0, 0, 0, 0, 0);
+ if (kevent64(loop->backend_fd, ev, 2, NULL, 0, 0, NULL))
return UV__ERR(errno);
return 0;
}
-static void uv__kqueue_delete(int kqfd, const struct kevent *ev) {
- struct kevent change;
+static void uv__kqueue_delete(int kqfd, const struct kevent64_s *ev) {
+ struct kevent64_s change;
- EV_SET(&change, ev->ident, ev->filter, EV_DELETE, 0, 0, 0);
+ EV_SET64(&change, ev->ident, ev->filter, EV_DELETE, 0, 0, 0, 0, 0);
- if (0 == kevent(kqfd, &change, 1, NULL, 0, NULL))
+ if (0 == kevent64(kqfd, &change, 1, NULL, 0, 0, NULL))
return;
if (errno == EBADF || errno == ENOENT)
@@ -155,11 +155,10 @@ static void uv__kqueue_delete(int kqfd, const struct kevent *ev) {
abort();
}
-
void uv__io_poll(uv_loop_t* loop, int timeout) {
uv__loop_internal_fields_t* lfields;
- struct kevent events[1024];
- struct kevent* ev;
+ struct kevent64_s events[1024];
+ struct kevent64_s* ev;
struct timespec spec;
unsigned int nevents;
unsigned int revents;
@@ -211,30 +210,30 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */
}
- EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0);
+ EV_SET64(events + nevents, w->fd, filter, op, fflags, 0, 0, 0, 0);
if (++nevents == ARRAY_SIZE(events)) {
- if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
+ if (kevent64(loop->backend_fd, events, nevents, NULL, 0, 0, NULL))
abort();
nevents = 0;
}
}
if ((w->events & POLLOUT) == 0 && (w->pevents & POLLOUT) != 0) {
- EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+ EV_SET64(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0, 0, 0);
if (++nevents == ARRAY_SIZE(events)) {
- if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
+ if (kevent64(loop->backend_fd, events, nevents, NULL, 0, 0, NULL))
abort();
nevents = 0;
}
}
if ((w->events & UV__POLLPRI) == 0 && (w->pevents & UV__POLLPRI) != 0) {
- EV_SET(events + nevents, w->fd, EV_OOBAND, EV_ADD, 0, 0, 0);
+ EV_SET64(events + nevents, w->fd, EV_OOBAND, EV_ADD, 0, 0, 0, 0, 0);
if (++nevents == ARRAY_SIZE(events)) {
- if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
+ if (kevent64(loop->backend_fd, events, nevents, NULL, 0, 0, NULL))
abort();
nevents = 0;
}
@@ -283,11 +282,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
*/
lfields->current_timeout = timeout;
- nfds = kevent(loop->backend_fd,
+ unsigned int flags = timeout == 0 && spec.tv_nsec == 0 && spec.tv_sec == 0
+ ? KEVENT_FLAG_IMMEDIATE : KEVENT_FLAG_NONE;
+
+ nfds = kevent64(loop->backend_fd,
events,
nevents,
events,
ARRAY_SIZE(events),
+ flags,
timeout == -1 ? NULL : &spec);
if (nfds == -1)
@@ -477,14 +480,14 @@ update_timeout:
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
- struct kevent* events;
+ struct kevent64_s* events;
uintptr_t i;
uintptr_t nfds;
assert(loop->watchers != NULL);
assert(fd >= 0);
- events = (struct kevent*) loop->watchers[loop->nwatchers];
+ events = (struct kevent64_s*) loop->watchers[loop->nwatchers];
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
if (events == NULL)
return;
@@ -498,7 +501,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
uv_fs_event_t* handle;
- struct kevent ev;
+ struct kevent64_s ev;
int events;
const char* path;
#if defined(F_GETPATH)
@@ -551,9 +554,9 @@ static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME
| NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
- EV_SET(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0);
+ EV_SET64(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0, 0, 0);
- if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
+ if (kevent64(loop->backend_fd, &ev, 1, NULL, 0, 0, NULL))
abort();
}
diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c
index 43e6b798..d7f06dc0 100644
--- a/deps/uv/src/unix/process.c
+++ b/deps/uv/src/unix/process.c
@@ -1035,9 +1035,9 @@ int uv_spawn(uv_loop_t* loop,
* with waitpid. */
if (exec_errorno == 0) {
#ifndef UV_USE_SIGCHLD
- struct kevent event;
- EV_SET(&event, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, 0);
- if (kevent(loop->backend_fd, &event, 1, NULL, 0, NULL)) {
+ struct kevent64_s event;
+ EV_SET64(&event, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, 0, 0, 0);
+ if (kevent64(loop->backend_fd, &event, 1, NULL, 0, 0, NULL)) {
if (errno != ESRCH)
abort();
/* Process already exited. Call waitpid on the next loop iteration. */
diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c
index 18763b47..84895d43 100644
--- a/deps/uv/src/unix/stream.c
+++ b/deps/uv/src/unix/stream.c
@@ -277,8 +277,8 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) {
* select(2) in separate thread for those fds
*/
- struct kevent filter[1];
- struct kevent events[1];
+ struct kevent64_s filter[1];
+ struct kevent64_s events[1];
struct timespec timeout;
uv__stream_select_t* s;
int fds[2];
@@ -296,14 +296,14 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) {
return UV__ERR(errno);
}
- EV_SET(&filter[0], *fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
+ EV_SET64(&filter[0], *fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0, 0, 0);
/* Use small timeout, because we only want to capture EINVALs */
timeout.tv_sec = 0;
timeout.tv_nsec = 1;
do
- ret = kevent(kq, filter, 1, events, 1, &timeout);
+ ret = kevent64(kq, filter, 1, events, 1, 0, &timeout);
while (ret == -1 && errno == EINTR);
uv__close(kq);
@corporatepiyush
Copy link

Great effort ! Its amazing to see how much is still there to improve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment