Skip to content

Instantly share code, notes, and snippets.

@billywhizz
Last active May 11, 2026 23:44
Show Gist options
  • Select an option

  • Save billywhizz/9e6e2ba9b672e4bd184a8285d732b469 to your computer and use it in GitHub Desktop.

Select an option

Save billywhizz/9e6e2ba9b672e4bd184a8285d732b469 to your computer and use it in GitHub Desktop.
node-main
node-ffi
bun
deno
lo
*.so

running

clang -O3 -fvisibility=hidden -march=native -mtune=native -fPIC -shared -o sum.so sum.c
./node-main --experimental-ffi --no-warnings node-ffi.js 30000000
./bun bun-ffi.js 200000000
./deno --allow-ffi deno-ffi.js 1000000000
./node-ffi --experimental-ffi --no-warnings node-ffi.js 1000000000
./lo lo-ffi.js 1000000000

results

name                             |             rate |          |
----------------------------------------------------------------
lo_0.0.23-pre.sum_ffi            |        632779231 |     1.00 | 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
node_v26.0.0-pre.sum_ffi         |        566253006 |     1.12 | 🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡
deno_2.7.14.sum_ffi              |        491923034 |     1.29 | 🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡
bun_1.3.13.sum_ffi               |        137802427 |     4.59 | 🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴
node_v27.0.0-pre.sum_ffi         |         18240166 |    34.69 | 🔴🔴


name                             |    rate_per_core |          |
----------------------------------------------------------------
lo_0.0.23-pre.sum_ffi            |        636491357 |     1.00 | 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
node_v26.0.0-pre.sum_ffi         |        566230241 |     1.12 | 🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡
deno_2.7.14.sum_ffi              |        490884852 |     1.30 | 🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡
bun_1.3.13.sum_ffi               |        135229076 |     4.71 | 🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴
node_v27.0.0-pre.sum_ffi         |         18221114 |    34.93 | 🔴🔴


name                             |          ns_iter |          |
----------------------------------------------------------------
lo_0.0.23-pre.sum_ffi            |             1.58 |     1.00 | 🟢🟢
node_v26.0.0-pre.sum_ffi         |             1.76 |     1.11 | 🟡🟡
deno_2.7.14.sum_ffi              |             2.03 |     1.28 | 🟡🟡
bun_1.3.13.sum_ffi               |             7.25 |     4.59 | 🟠🟠🟠🟠🟠🟠🟠
node_v27.0.0-pre.sum_ffi         |            54.82 |    34.70 | 🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴


name                             |              rss |          |
----------------------------------------------------------------
lo_0.0.23-pre.sum_ffi            |         30048256 |     1.00 | 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
node_v26.0.0-pre.sum_ffi         |         50413568 |     1.68 | 🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡
node_v27.0.0-pre.sum_ffi         |         51085312 |     1.70 | 🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡
deno_2.7.14.sum_ffi              |         66535424 |     2.21 | 🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠
bun_1.3.13.sum_ffi               |         78528512 |     2.61 | 🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠
/*
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,
};
node_v27.0.0-pre sum_ffi time 1657 rate 18101210 rate/core 18086625 ns/iter 55.24 rss 49676288 usr 100.00 sys 0.08 tot 100.08
node_v27.0.0-pre sum_ffi time 1644 rate 18240166 rate/core 18221114 ns/iter 54.82 rss 50937856 usr 100.04 sys 0.06 tot 100.10
node_v27.0.0-pre sum_ffi time 1828 rate 16406965 rate/core 16401016 ns/iter 60.94 rss 50937856 usr 100.00 sys 0.03 tot 100.04
node_v27.0.0-pre sum_ffi time 1829 rate 16394503 rate/core 16393912 ns/iter 60.99 rss 50954240 usr 99.98 sys 0.03 tot 100.00
node_v27.0.0-pre sum_ffi time 1829 rate 16395182 rate/core 16395746 ns/iter 60.99 rss 50970624 usr 99.97 sys 0.03 tot 100.00
node_v27.0.0-pre sum_ffi time 1826 rate 16426183 rate/core 16431408 ns/iter 60.87 rss 50970624 usr 99.94 sys 0.03 tot 99.97
node_v27.0.0-pre sum_ffi time 1833 rate 16365366 rate/core 16363330 ns/iter 61.10 rss 50970624 usr 99.97 sys 0.04 tot 100.01
node_v27.0.0-pre sum_ffi time 1837 rate 16325243 rate/core 16325279 ns/iter 61.25 rss 50987008 usr 99.97 sys 0.03 tot 100.00
node_v27.0.0-pre sum_ffi time 1840 rate 16304048 rate/core 16302143 ns/iter 61.33 rss 51003392 usr 99.98 sys 0.03 tot 100.01
node_v27.0.0-pre sum_ffi time 1842 rate 16280475 rate/core 16279397 ns/iter 61.42 rss 51085312 usr 99.97 sys 0.03 tot 100.01
bun_1.3.13 sum_ffi time 1451 rate 137802427 rate/core 135229076 ns/iter 7.25 rss 77348864 usr 100.09 sys 1.81 tot 101.90
bun_1.3.13 sum_ffi time 1486 rate 134553955 rate/core 131912683 ns/iter 7.43 rss 78430208 usr 100.39 sys 1.62 tot 102.00
bun_1.3.13 sum_ffi time 1495 rate 133728759 rate/core 131731651 ns/iter 7.47 rss 78479360 usr 99.92 sys 1.59 tot 101.52
bun_1.3.13 sum_ffi time 1467 rate 136278896 rate/core 134177933 ns/iter 7.33 rss 78495744 usr 99.90 sys 1.67 tot 101.57
bun_1.3.13 sum_ffi time 1473 rate 135723750 rate/core 133585297 ns/iter 7.36 rss 78512128 usr 99.95 sys 1.65 tot 101.60
bun_1.3.13 sum_ffi time 1487 rate 134417154 rate/core 132366891 ns/iter 7.43 rss 78528512 usr 99.93 sys 1.61 tot 101.55
bun_1.3.13 sum_ffi time 1488 rate 134356372 rate/core 132323618 ns/iter 7.44 rss 78446592 usr 99.87 sys 1.66 tot 101.54
bun_1.3.13 sum_ffi time 1480 rate 135114446 rate/core 132967736 ns/iter 7.40 rss 78249984 usr 99.95 sys 1.66 tot 101.61
bun_1.3.13 sum_ffi time 1480 rate 135083468 rate/core 133055118 ns/iter 7.40 rss 78282752 usr 99.90 sys 1.63 tot 101.52
bun_1.3.13 sum_ffi time 1477 rate 135359363 rate/core 133308593 ns/iter 7.38 rss 78299136 usr 99.92 sys 1.62 tot 101.54
deno_2.7.14 sum_ffi time 2314 rate 432037286 rate/core 433907578 ns/iter 2.31 rss 65290240 usr 99.57 sys 0.00 tot 99.57
deno_2.7.14 sum_ffi time 2038 rate 490567401 rate/core 488162659 ns/iter 2.03 rss 65650688 usr 100.49 sys 0.00 tot 100.49
deno_2.7.14 sum_ffi time 2032 rate 491923034 rate/core 489523410 ns/iter 2.03 rss 65880064 usr 100.00 sys 0.49 tot 100.49
deno_2.7.14 sum_ffi time 2037 rate 490884852 rate/core 490884852 ns/iter 2.03 rss 65912832 usr 100.00 sys 0.00 tot 100.00
deno_2.7.14 sum_ffi time 2046 rate 488668969 rate/core 488668969 ns/iter 2.04 rss 65847296 usr 100.00 sys 0.00 tot 100.00
deno_2.7.14 sum_ffi time 2058 rate 485732322 rate/core 485732322 ns/iter 2.05 rss 65847296 usr 100.00 sys 0.00 tot 100.00
deno_2.7.14 sum_ffi time 2059 rate 485591637 rate/core 485591637 ns/iter 2.05 rss 65847296 usr 100.00 sys 0.00 tot 100.00
deno_2.7.14 sum_ffi time 2055 rate 486595462 rate/core 486595462 ns/iter 2.05 rss 65863680 usr 100.00 sys 0.00 tot 100.00
deno_2.7.14 sum_ffi time 2059 rate 485585663 rate/core 485585663 ns/iter 2.05 rss 66256896 usr 100.00 sys 0.00 tot 100.00
deno_2.7.14 sum_ffi time 2060 rate 485367976 rate/core 485367976 ns/iter 2.06 rss 66535424 usr 100.00 sys 0.00 tot 100.00
node_v26.0.0-pre sum_ffi time 2367 rate 422312858 rate/core 422125148 ns/iter 2.36 rss 49201152 usr 99.99 sys 0.06 tot 100.04
node_v26.0.0-pre sum_ffi time 2386 rate 418936752 rate/core 418669035 ns/iter 2.38 rss 50118656 usr 100.03 sys 0.03 tot 100.06
node_v26.0.0-pre sum_ffi time 1781 rate 561472252 rate/core 561423392 ns/iter 1.78 rss 50118656 usr 99.98 sys 0.03 tot 100.01
node_v26.0.0-pre sum_ffi time 1773 rate 563844448 rate/core 563953169 ns/iter 1.77 rss 50135040 usr 99.95 sys 0.03 tot 99.98
node_v26.0.0-pre sum_ffi time 1766 rate 566121093 rate/core 565936506 ns/iter 1.76 rss 50135040 usr 100.00 sys 0.03 tot 100.03
node_v26.0.0-pre sum_ffi time 1765 rate 566253006 rate/core 566230241 ns/iter 1.76 rss 50135040 usr 99.97 sys 0.03 tot 100.00
node_v26.0.0-pre sum_ffi time 1767 rate 565862733 rate/core 565782365 ns/iter 1.76 rss 50135040 usr 99.97 sys 0.04 tot 100.01
node_v26.0.0-pre sum_ffi time 1769 rate 565157543 rate/core 565266126 ns/iter 1.76 rss 50266112 usr 99.95 sys 0.03 tot 99.98
node_v26.0.0-pre sum_ffi time 1767 rate 565785588 rate/core 565906260 ns/iter 1.76 rss 50298880 usr 99.96 sys 0.02 tot 99.98
node_v26.0.0-pre sum_ffi time 1771 rate 564482428 rate/core 564175970 ns/iter 1.77 rss 50413568 usr 100.02 sys 0.03 tot 100.05
lo_0.0.23-pre sum_ffi time 1808 rate 552806572 rate/core 552806572 ns/iter 1.80 rss 27475968 usr 100.00 sys 0.00 tot 100.00
lo_0.0.23-pre sum_ffi time 1778 rate 562167268 rate/core 562167268 ns/iter 1.77 rss 28786688 usr 100.00 sys 0.00 tot 100.00
lo_0.0.23-pre sum_ffi time 1580 rate 632779231 rate/core 632779231 ns/iter 1.58 rss 30015488 usr 100.00 sys 0.00 tot 100.00
lo_0.0.23-pre sum_ffi time 1581 rate 632488267 rate/core 636491357 ns/iter 1.58 rss 30015488 usr 99.37 sys 0.00 tot 99.37
lo_0.0.23-pre sum_ffi time 1584 rate 630921992 rate/core 630921992 ns/iter 1.58 rss 30015488 usr 100.00 sys 0.00 tot 100.00
lo_0.0.23-pre sum_ffi time 1586 rate 630307187 rate/core 630307187 ns/iter 1.58 rss 30015488 usr 100.00 sys 0.00 tot 100.00
lo_0.0.23-pre sum_ffi time 1586 rate 630404140 rate/core 630404140 ns/iter 1.58 rss 30015488 usr 100.00 sys 0.00 tot 100.00
lo_0.0.23-pre sum_ffi time 1587 rate 629855398 rate/core 629855398 ns/iter 1.58 rss 30015488 usr 100.00 sys 0.00 tot 100.00
lo_0.0.23-pre sum_ffi time 1587 rate 630105827 rate/core 630105827 ns/iter 1.58 rss 30015488 usr 100.00 sys 0.00 tot 100.00
lo_0.0.23-pre sum_ffi time 1583 rate 631474190 rate/core 631474190 ns/iter 1.58 rss 30048256 usr 100.00 sys 0.00 tot 100.00
import { dlopen, FFIType, ptr } from "bun:ffi";
import { Bench } from './bench.js'
const { symbols: {
sum
} } = dlopen('./sum.so', {
sum: { args: [FFIType.i32, FFIType.i32], returns: FFIType.i32, },
})
const bench = new Bench()
const iter = 10
const runs = parseInt(process.argv[2] || '200000000', 10)
{
for (let i = 0; i < iter; i++) {
bench.start('sum_ffi')
for (let j = 0; j < runs; j++) assert(sum(10, 20) === 30)
bench.end(runs)
}
}
import { colors } from './bench.js'
import { readFileSync } from 'node:fs'
const { AM, AY, AG, AD } = colors
function uniq (arr) {
return Object.keys(arr.reduce((p, c) => {
p[c] = c
return p
}, {}))
}
function sort_ascending (a, b) {
return a[1] - b[1]
}
function sort_descending (a, b) {
return b[1] - a[1]
}
const Directions = {
Ascending: 0,
Descending: 1
}
function get_dot (pc, direction = Directions.Ascending) {
if (direction === Directions.Ascending) {
if (pc <= 1.1) return dots[0]
if (pc <= 2.0) return dots[1]
if (pc <= 5.0) return dots[2]
return dots[3]
} else {
if (pc >= 0.99) return dots[0]
if (pc >= 0.75) return dots[1]
if (pc >= 0.40) return dots[2]
return dots[3]
}
}
function showChart (name, direction = Directions.Ascending) {
const results = Object.keys(summary).map(k => [k, summary[k][name]])
let best = 0
let max = 0
if (direction === Directions.Ascending) {
best = Math.min(...results.map(v => v[1]))
max = Math.max(...results.map(v => v[1]))
results.sort(sort_ascending)
} else {
best = Math.max(...results.map(v => v[1]))
max = best
results.sort(sort_descending)
}
console.log('')
console.log(`${AY}${'name'.padEnd(32, ' ')}${AD} | ${AY}${name.padStart(16, ' ')}${AD} | ${AY}${''.padStart(8, ' ')}${AD} |`)
console.log('-'.repeat(64))
for (const result of results) {
const [name, value] = result
const blocks = Math.ceil((value / max) * 50)
const pc = direction === Directions.Descending ? ((best / value)) : ((value / best))
const dot = get_dot(value / best, direction)
const display_val = value.toFixed(value >= 1000 ? 0 : 2)
console.log(`${AM}${name.slice(0, 32).padEnd(32, ' ')}${AD} | ${AD}${display_val.padStart(16, ' ')}${AD} | ${AG}${pc.toFixed(2).padStart(8, ' ')}${AD} | ${(new Array(blocks)).fill(dot).join('')}`)
}
console.log('')
}
const dots = ['🟢', '🟡', '🟠', '🔴']
const summary = {}
const results = readFileSync('bench.log', 'utf-8')
.split('\n')
.map(l => l.trim())
.filter(l => l)
.map(l => l.match(/([\w\d\.\_\-]+)\s+([\w\_\-]+)\s+time\s+(\d+)\s+rate\s+(\d+)\s+rate\/core\s+(\d+)\s+ns\/iter\s+([\d\.]+)\s+rss\s+(\d+)\s+usr\s+([\d\.]+)\s+sys\s+([\d\.]+)\s+tot\s+([\d\.]+)/))
.filter(m => m)
.map(matches => {
const [ runtime, benchmark, time, rate, rate_per_core, ns_iter, rss, usr, sys, tot ] = Array.from(matches).slice(1).map((v, i) => i < 2 ? v : Number(v))
return { name: `${runtime}.${benchmark}`, runtime, benchmark, time, rate, rate_per_core, ns_iter, rss, usr, sys, tot }
})
uniq(results.map(result => `${result.runtime}.${result.benchmark}`)).forEach(name => {
const b = summary[name] = {
rate: 0, rate_per_core: 0, ns_iter: Infinity, rss: 0
}
for (const result of results.filter(r => r.name === name)) {
const { rate, rate_per_core, ns_iter, rss } = result
if (rate > b.rate) b.rate = rate
if (rate_per_core > b.rate_per_core) b.rate_per_core = rate_per_core
if (ns_iter < b.ns_iter) b.ns_iter = ns_iter
if (rss > b.rss) b.rss = rss
}
})
showChart('rate', Directions.Descending)
showChart('rate_per_core', Directions.Descending)
showChart('ns_iter', Directions.Ascending)
showChart('rss', Directions.Ascending)
import { Bench } from './bench.js'
const {
symbols: { sum },
} = Deno.dlopen("./sum.so", {
sum: {
parameters: ['i32', 'i32'],
result: 'i32'
}
})
const bench = new Bench()
const iter = 10
const runs = parseInt(process.argv[2] || '1000000000', 10)
{
for (let i = 0; i < iter; i++) {
bench.start('sum_ffi')
for (let j = 0; j < runs; j++) assert(sum(10, 20) === 30)
bench.end(runs)
}
}
import { Bench } from './bench.js'
import { generate_callback, bind } from 'lib/ffi.js'
const { assert, core } = lo
const { dlopen, dlsym, RTLD_NOW, RTLD_LOCAL } = core
const handle = assert(dlopen('./sum.so', RTLD_NOW | RTLD_LOCAL))
const sum = bind(assert(dlsym(handle, 'sum')), 'i32', ['i32', 'i32'])
const sum_slow = bind(assert(dlsym(handle, 'sum')), 'i32', ['i32', 'i32'], true)
const bench = new Bench()
const iter = 10
const runs = parseInt(lo.args[2] || '1000000000', 10)
{
for (let i = 0; i < iter; i++) {
bench.start('sum_ffi')
for (let j = 0; j < runs; j++) assert(sum(10, 20) === 30)
bench.end(runs)
}
}
import { Bench } from './bench.js'
import { DynamicLibrary } from "node:ffi";
// node --experimental-ffi --no-warnings test-ffi.js
const lib = new DynamicLibrary('./sum.so');
const sum = lib.getFunction('sum', {
parameters: ['i32', 'i32'],
result: 'i32',
});
const bench = new Bench()
const iter = 10
const runs = parseInt(process.argv[2] || '30000000', 10)
{
for (let i = 0; i < iter; i++) {
bench.start('sum_ffi')
for (let j = 0; j < runs; j++) assert(sum(10, 20) === 30)
bench.end(runs)
}
}
#include <stdlib.h>
// clang -O3 -fvisibility=hidden -march=native -mtune=native -fPIC -shared -o sum.so sum.c
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
typedef unsigned int (*callback)(unsigned int);
unsigned int counter = 0;
DLL_PUBLIC unsigned int test(callback cb) {
counter = cb(counter);
return counter;
}
DLL_PUBLIC int sum (int a, int b) {
return a + b;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment