TOC

JavaScript 读文件的 5 种方法

性能测试

看到一篇英文博客《How to read files quickly in JavaScript》对 JS 常用的 5 中文件读取方法做一个性能测试:

测试方案:同一个文件(大约 1KB),读 50000 次,看耗时情况。
测试环境:一台几十个 Intel 核心和充足内存的服务器。
语言环境:Node 20.1 和 Bun 1.0.14

  • test1.js (fs/promises)

    const fs = require("fs/promises");
    const readFile = fs.readFile;
    let start = Date.now();
    let count = 0;
    const N = 50000;
    for (let i = 0; i < N; i++) {
        readFile("lipsum.txt", { encoding: "utf-8" })
            .then((data) => {
                if (data.length != 1335) throw 1;
                count++;
                if (count == N) {
                    end = Date.now();
                    console.log("time: ", end - start);
                }
            })
            .catch((err) => {
                throw 1;
            });
    }
    
  • test2.js (fs.readFile + util.promisify)

    const fs = require("fs");
    const util = require("util");
    const readFile = util.promisify(fs.readFile);
    let start = Date.now();
    let count = 0;
    const N = 50000;
    for (let i = 0; i < N; i++) {
        readFile("lipsum.txt", { encoding: "utf-8" })
            .then((data) => {
                if (data.length != 1335) throw 1;
                count++;
                if (count == N) {
                    end = Date.now();
                    console.log("time: ", end - start);
                }
            })
            .catch((err) => {
                throw 1;
            });
    }
    
  • test3.js (fs.readFileSync)

    const fs = require("fs");
    const readFileSync = fs.readFileSync;
    let start = Date.now();
    let count = 0;
    const N = 50000;
    for (let i = 0; i < N; i++) {
        var data = readFileSync("lipsum.txt", { encoding: "utf-8" });
        if (data.length != 1335) throw 1;
    }
    console.log("time: ", Date.now() - start);
    
  • test4.js (fs.readFileSync + promise)

    const fs = require("fs");
    const readFileSync = fs.readFileSync;
    async function f(name, options) {
        return readFileSync(name, options);
    }
    let start = Date.now();
    let count = 0;
    const N = 50000;
    for (let i = 0; i < N; i++) {
        f("lipsum.txt", { encoding: "utf-8" })
            .then((data) => {
                if (data.length != 1335) throw 1;
                count++;
                if (count == N) {
                    end = Date.now();
                    console.log("time: ", end - start);
                }
            })
            .catch((err) => {
                throw 1;
            });
    }
    
  • test5.js (fs.readFile)

    const fs = require("fs");
    const readFile = fs.readFile;
    let start = Date.now();
    let count = 0;
    const N = 50000;
    for (let i = 0; i < N; i++) {
        fs.readFile("lipsum.txt", function read(err, data) {
            if (err) {
                throw 1;
            }
            if (data.length != 1335) throw 1;
            count++;
            if (count == N) {
                end = Date.now();
                console.log("time: ", end - start);
                start = end;
            }
        });
    }
    
  • 测试脚本

    for i in {1..10}; do
    echo "trial ${i}"
    echo "node"
    node --version
    node test1.js > /dev/null
    node test1.js
    node test2.js > /dev/null
    node test2.js
    node test3.js > /dev/null
    node test3.js
    node test4.js > /dev/null
    node test4.js
    node test5.js > /dev/null
    node test5.js
    echo "Bun"
    bun run test1.js > /dev/null
    bun run test1.js
    bun run test2.js > /dev/null
    bun run test2.js
    bun run test3.js > /dev/null
    bun run test3.js
    bun run test4.js > /dev/null
    bun run test4.js
    bun run test5.js > /dev/null
    bun run test5.js
    done
    

PS:以上代码在 GitHub 上可以找到。

测试结果

  Time (Node) Time (Bun)
fs/promises 2400 ms 110 ms
fs.readFile & util.promisify 1500 ms 180 ms
fs.readFileSync 140 ms 140 ms
await fs.readFileSync 220 ms 180 ms
fs.readFile 760 ms 90 ms

说明:

  1. Node 的 promise 不行(Bun 好得多)
  2. 文件增大到 32KB 测试,Node 会报 heap limit Allocation failed(堆限制分配失败)
    Bun 没有问题,但是结论还是不受影响。
如果你有魔法,你可以看到一个评论框~