fs: add c++ fast path for writeFileSync string utf8
Summary
Added fast path in almost entirely C++ for writeFileSync
with UTF8 encoding and string data. Also improves appendFileSync
as it just uses writeFileSync
under the hood. Only string data as Buffer seems questionable in benchmarks for this so I'll just leave it for strings for now.
TL;DR: This makes writeFileSync(path, data: string)
UTF8 up to ~2.5x faster depending on data size, especially when using file descriptors
Bench results
Benchmark in this PR
Note: running locally on Linux/i9/SSD
confidence improvement accuracy (*) (**) (***)
fs/bench-writeFileSync.js n=1000 func='writeFile' useBuffer='false' length=1024 useFd='false' encoding='utf8' -0.37 % ±4.40% ±5.90% ±7.77%
fs/bench-writeFileSync.js n=1000 func='writeFile' useBuffer='false' length=1024 useFd='true' encoding='utf8' *** 49.15 % ±5.11% ±6.82% ±8.91%
fs/bench-writeFileSync.js n=1000 func='writeFile' useBuffer='false' length=102400 useFd='false' encoding='utf8' *** 6.69 % ±1.67% ±2.22% ±2.89%
fs/bench-writeFileSync.js n=1000 func='writeFile' useBuffer='false' length=102400 useFd='true' encoding='utf8' *** 74.52 % ±4.35% ±5.85% ±7.74%
fs/bench-writeFileSync.js n=1000 func='writeFile' useBuffer='false' length=1048576 useFd='false' encoding='utf8' *** 16.05 % ±1.60% ±2.14% ±2.81%
fs/bench-writeFileSync.js n=1000 func='writeFile' useBuffer='false' length=1048576 useFd='true' encoding='utf8' *** 85.82 % ±3.58% ±4.77% ±6.23%
Benchmark CI (old): https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1421/
Alternative benchmark
Alternative benchmark using Bun's bench for fs.copyFileSync
(string data, using paths)
Current (main)
benchmark time (avg) (min … max) p75 p99 p995
------------------------------------------------------ -----------------------------
12 ascii 2.19 µs/iter (2.07 µs … 2.67 µs) 2.21 µs 2.67 µs 2.67 µs
12 utf8 2.23 µs/iter (2.08 µs … 2.32 µs) 2.26 µs 2.32 µs 2.32 µs
12288 ascii 6.14 µs/iter (5.97 µs … 6.62 µs) 6.14 µs 6.62 µs 6.62 µs
18432 utf8 40.02 µs/iter (36.13 µs … 204.16 µs) 40.05 µs 63.6 µs 69.01 µs
This PR
Long ascii: 6.14µs
-> 3.46µs
(~1.8x speedup)
Long utf8: 40.02µs
-> 18.25µs
(~2.2x speedup)
benchmark time (avg) (min … max) p75 p99 p995
------------------------------------------------------ -----------------------------
12 ascii 1.97 µs/iter (1.9 µs … 2.03 µs) 1.98 µs 2.03 µs 2.03 µs
12 utf8 1.95 µs/iter (1.91 µs … 2 µs) 1.97 µs 2 µs 2 µs
12288 ascii 3.46 µs/iter (3.39 µs … 3.6 µs) 3.47 µs 3.6 µs 3.6 µs
18432 utf8 18.25 µs/iter (16.06 µs … 147.95 µs) 18.48 µs 21.77 µs 24.03 µs