Skip to content

Commit e419567

Browse files
perf: fast-path system_fs canonicalization and small file I/O
1 parent cdbaf67 commit e419567

15 files changed

Lines changed: 484 additions & 26 deletions

Cargo.lock

Lines changed: 58 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bench/run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ cd "$(dirname "$0")"
2626
ESRUN="${ESRUN:-../target/release/esrun}"
2727
STARTUP_RUNS="${STARTUP_RUNS:-25}"
2828
WORKLOAD_RUNS="${WORKLOAD_RUNS:-5}"
29-
ALL_WORKLOADS="compute json jsonbig sha256 crypto url encoding base64 structured async timers streams fetch http fsread fswrite fsappend glob"
29+
ALL_WORKLOADS="compute json jsonbig sha256 crypto url encoding base64 structured async timers streams fetch http fsread_small fsread_large fswrite_small fswrite_large fsappend_small fsappend_large fsstat_small fsstat_large fsexists_small fsexists_large glob"
3030
WORKLOADS="${WORKLOADS:-$ALL_WORKLOADS}"
3131
BENCH_JSON="${BENCH_JSON:-}"
3232
FETCH_PORT=18923

bench/scripts/fsappend_large.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// File append benchmark: append 2 MB to a growing file in a loop. The
2+
// filesystem is not a shared Web API, so each runtime uses its own surface.
3+
(async () => {
4+
const N = 20;
5+
const data = "x".repeat(2097152);
6+
const tmp = "bench_fsappend.bin";
7+
8+
let append, cleanup;
9+
if (typeof Deno !== "undefined") {
10+
const enc = new TextEncoder();
11+
append = (p, d) => Deno.writeFile(p, enc.encode(d), { append: true });
12+
cleanup = (p) => Deno.remove(p).catch(() => {});
13+
} else if (typeof Bun !== "undefined") {
14+
const { appendFile, unlink } = await import("node:fs/promises");
15+
append = (p, d) => appendFile(p, d);
16+
cleanup = (p) => unlink(p).catch(() => {});
17+
} else if (typeof process !== "undefined" && process.versions && process.versions.node) {
18+
const fsp = await import("node:fs/promises");
19+
append = (p, d) => fsp.appendFile(p, d);
20+
cleanup = (p) => fsp.unlink(p).catch(() => {});
21+
} else {
22+
const fs = await import("runtime:fs");
23+
append = (p, d) => fs.write(p, d, { append: true });
24+
cleanup = (p) => fs.remove(p).catch(() => {});
25+
}
26+
27+
await cleanup(tmp); // start fresh
28+
const run = async (n) => {
29+
for (let i = 0; i < n; i++) await append(tmp, data);
30+
};
31+
await run(1); // untimed warmup
32+
const t0 = performance.now();
33+
await run(N);
34+
const t1 = performance.now();
35+
await cleanup(tmp);
36+
console.log("RESULT_MS=" + (t1 - t0).toFixed(2));
37+
})();

bench/scripts/fsexists_large.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(async () => {
2+
const N = 20;
3+
const tmp = "bench_fsexists.bin";
4+
5+
let exists, write, cleanup;
6+
if (typeof Deno !== "undefined") {
7+
const enc = new TextEncoder();
8+
write = (p, d) => Deno.writeFile(p, enc.encode(d));
9+
exists = (p) => Deno.stat(p).then(()=>true).catch(()=>false);
10+
cleanup = (p) => Deno.remove(p).catch(() => {});
11+
} else if (typeof Bun !== "undefined") {
12+
const { stat, unlink } = await import("node:fs/promises");
13+
write = (p, d) => Bun.write(p, d);
14+
exists = (p) => stat(p).then(()=>true).catch(()=>false);
15+
cleanup = (p) => unlink(p).catch(() => {});
16+
} else if (typeof process !== "undefined" && process.versions && process.versions.node) {
17+
const fsp = await import("node:fs/promises");
18+
write = (p, d) => fsp.writeFile(p, d);
19+
exists = (p) => fsp.stat(p).then(()=>true).catch(()=>false);
20+
cleanup = (p) => fsp.unlink(p).catch(() => {});
21+
} else {
22+
const fs = await import("runtime:fs");
23+
write = (p, d) => fs.write(p, d);
24+
exists = (p) => fs.exists(p);
25+
cleanup = (p) => fs.remove(p).catch(() => {});
26+
}
27+
28+
await write(tmp, "x".repeat(2097152));
29+
const run = async (n) => {
30+
for (let i = 0; i < n; i++) await exists(tmp);
31+
};
32+
await run(N / 10);
33+
const t0 = performance.now();
34+
await run(N);
35+
const t1 = performance.now();
36+
await cleanup(tmp);
37+
console.log("RESULT_MS=" + (t1 - t0).toFixed(2));
38+
})();

bench/scripts/fsexists_small.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(async () => {
2+
const N = 5000;
3+
const tmp = "bench_fsexists.bin";
4+
5+
let exists, write, cleanup;
6+
if (typeof Deno !== "undefined") {
7+
const enc = new TextEncoder();
8+
write = (p, d) => Deno.writeFile(p, enc.encode(d));
9+
exists = (p) => Deno.stat(p).then(()=>true).catch(()=>false);
10+
cleanup = (p) => Deno.remove(p).catch(() => {});
11+
} else if (typeof Bun !== "undefined") {
12+
const { stat, unlink } = await import("node:fs/promises");
13+
write = (p, d) => Bun.write(p, d);
14+
exists = (p) => stat(p).then(()=>true).catch(()=>false);
15+
cleanup = (p) => unlink(p).catch(() => {});
16+
} else if (typeof process !== "undefined" && process.versions && process.versions.node) {
17+
const fsp = await import("node:fs/promises");
18+
write = (p, d) => fsp.writeFile(p, d);
19+
exists = (p) => fsp.stat(p).then(()=>true).catch(()=>false);
20+
cleanup = (p) => fsp.unlink(p).catch(() => {});
21+
} else {
22+
const fs = await import("runtime:fs");
23+
write = (p, d) => fs.write(p, d);
24+
exists = (p) => fs.exists(p);
25+
cleanup = (p) => fs.remove(p).catch(() => {});
26+
}
27+
28+
await write(tmp, "x".repeat(4096));
29+
const run = async (n) => {
30+
for (let i = 0; i < n; i++) await exists(tmp);
31+
};
32+
await run(N / 10);
33+
const t0 = performance.now();
34+
await run(N);
35+
const t1 = performance.now();
36+
await cleanup(tmp);
37+
console.log("RESULT_MS=" + (t1 - t0).toFixed(2));
38+
})();

bench/scripts/fsread_large.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// File read benchmark: read a 2 MB file in a loop (written once, untimed). The
2+
// filesystem is not a shared Web API, so each runtime uses its own surface.
3+
(async () => {
4+
const N = 20;
5+
const data = "x".repeat(2097152);
6+
const tmp = "bench_fsread.bin";
7+
8+
let write, read, cleanup;
9+
if (typeof Deno !== "undefined") {
10+
const enc = new TextEncoder();
11+
write = (p, d) => Deno.writeFile(p, enc.encode(d));
12+
read = (p) => Deno.readFile(p);
13+
cleanup = (p) => Deno.remove(p).catch(() => {});
14+
} else if (typeof Bun !== "undefined") {
15+
const { unlink } = await import("node:fs/promises");
16+
write = (p, d) => Bun.write(p, d);
17+
read = (p) => Bun.file(p).arrayBuffer();
18+
cleanup = (p) => unlink(p).catch(() => {});
19+
} else if (typeof process !== "undefined" && process.versions && process.versions.node) {
20+
const fsp = await import("node:fs/promises");
21+
write = (p, d) => fsp.writeFile(p, d);
22+
read = (p) => fsp.readFile(p);
23+
cleanup = (p) => fsp.unlink(p).catch(() => {});
24+
} else {
25+
const fs = await import("runtime:fs");
26+
write = (p, d) => fs.write(p, d);
27+
read = (p) => fs.file(p).arrayBuffer();
28+
cleanup = (p) => fs.remove(p).catch(() => {});
29+
}
30+
31+
await write(tmp, data); // setup, untimed
32+
const run = async (n) => {
33+
for (let i = 0; i < n; i++) await read(tmp);
34+
};
35+
await run(1); // untimed warmup
36+
const t0 = performance.now();
37+
await run(N);
38+
const t1 = performance.now();
39+
await cleanup(tmp);
40+
console.log("RESULT_MS=" + (t1 - t0).toFixed(2));
41+
})();

bench/scripts/fsstat_large.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(async () => {
2+
const N = 20;
3+
const tmp = "bench_fsstat.bin";
4+
5+
let stat, write, cleanup;
6+
if (typeof Deno !== "undefined") {
7+
const enc = new TextEncoder();
8+
write = (p, d) => Deno.writeFile(p, enc.encode(d));
9+
stat = (p) => Deno.stat(p);
10+
cleanup = (p) => Deno.remove(p).catch(() => {});
11+
} else if (typeof Bun !== "undefined") {
12+
const { stat: statP, unlink } = await import("node:fs/promises");
13+
write = (p, d) => Bun.write(p, d);
14+
stat = (p) => statP(p);
15+
cleanup = (p) => unlink(p).catch(() => {});
16+
} else if (typeof process !== "undefined" && process.versions && process.versions.node) {
17+
const fsp = await import("node:fs/promises");
18+
write = (p, d) => fsp.writeFile(p, d);
19+
stat = (p) => fsp.stat(p);
20+
cleanup = (p) => fsp.unlink(p).catch(() => {});
21+
} else {
22+
const fs = await import("runtime:fs");
23+
write = (p, d) => fs.write(p, d);
24+
stat = (p) => fs.stat(p);
25+
cleanup = (p) => fs.remove(p).catch(() => {});
26+
}
27+
28+
await write(tmp, "x".repeat(2097152));
29+
const run = async (n) => {
30+
for (let i = 0; i < n; i++) await stat(tmp);
31+
};
32+
await run(N / 10);
33+
const t0 = performance.now();
34+
await run(N);
35+
const t1 = performance.now();
36+
await cleanup(tmp);
37+
console.log("RESULT_MS=" + (t1 - t0).toFixed(2));
38+
})();

bench/scripts/fsstat_small.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(async () => {
2+
const N = 5000;
3+
const tmp = "bench_fsstat.bin";
4+
5+
let stat, write, cleanup;
6+
if (typeof Deno !== "undefined") {
7+
const enc = new TextEncoder();
8+
write = (p, d) => Deno.writeFile(p, enc.encode(d));
9+
stat = (p) => Deno.stat(p);
10+
cleanup = (p) => Deno.remove(p).catch(() => {});
11+
} else if (typeof Bun !== "undefined") {
12+
const { stat: statP, unlink } = await import("node:fs/promises");
13+
write = (p, d) => Bun.write(p, d);
14+
stat = (p) => statP(p);
15+
cleanup = (p) => unlink(p).catch(() => {});
16+
} else if (typeof process !== "undefined" && process.versions && process.versions.node) {
17+
const fsp = await import("node:fs/promises");
18+
write = (p, d) => fsp.writeFile(p, d);
19+
stat = (p) => fsp.stat(p);
20+
cleanup = (p) => fsp.unlink(p).catch(() => {});
21+
} else {
22+
const fs = await import("runtime:fs");
23+
write = (p, d) => fs.write(p, d);
24+
stat = (p) => fs.stat(p);
25+
cleanup = (p) => fs.remove(p).catch(() => {});
26+
}
27+
28+
await write(tmp, "x".repeat(4096));
29+
const run = async (n) => {
30+
for (let i = 0; i < n; i++) await stat(tmp);
31+
};
32+
await run(N / 10);
33+
const t0 = performance.now();
34+
await run(N);
35+
const t1 = performance.now();
36+
await cleanup(tmp);
37+
console.log("RESULT_MS=" + (t1 - t0).toFixed(2));
38+
})();

0 commit comments

Comments
 (0)