拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Deno fs 基准测试实战:tests/bench/fs 中 Deno 与 Node 文件 API 的对比测量方法

Deno fs 基准测试实战tests/bench/fs 中 Deno 与 Node 文件 API 的对比测量方法【免费下载链接】denoA modern runtime for JavaScript and TypeScript.项目地址: https://gitcode.com/GitHub_Trending/de/deno本文基于 Deno 仓库中的 tests/bench/fs/README.md讲解如何使用仓库自带的run.mjs脚本对Deno.*与 Node.jsfs模块的同步文件 API 做同口径的性能对比如何运行基准、测量框架bench()的迭代与速率计算逻辑、如何按规范新增基准项并结合ext/fs扩展源码说明被测函数如copyFileSync在 Deno 内部的完整调用链。读完本文你可以独立跑通这组跨运行时基准并理解其结果数据deno.json/node.json的生成方式。fs 基准在仓库中的位置这组基准位于 tests/bench/fs/目录内只有两个文件tests/bench/fs/README.md说明如何新增基准、如何运行tests/bench/fs/run.mjs基准脚本本体同一份代码可在 Deno 和 Node 下分别运行。它与同目录下其他纯 Rust 基准如 tests/bench/main.rs 中通过cargo bench --bench deno_bench执行的 exec_time、binary_size、strace、mem_usage 等类别见 tests/bench/README.md不同fs 基准是JS 层脚本目的是在相同硬件上对比两个运行时的同类文件 API脚本通过检测全局对象自动识别当前运行时const runtime typeof Deno ! undefined ? deno : node;运行方式README 给出的原始命令是deno run -A --unstable run.mjs node run.js需要注意两点实操细节当前仓库中该脚本的文件名是 run.mjs顶层await import(fs)表明它是 ES 模块在仓库的tests/bench/fs/目录下执行时Deno 侧命令应写作deno run -A --unstable run.mjs-A等价于--allow-all不是可选项脚本会写入test文件并读取、修改、删除其元数据若不加权限标志Deno.copyFileSync等操作会在权限检查阶段直接失败——这一点在源码中有直接证据见下文“源码纵深”一节。脚本运行结束后会按运行时名将全部结果写为 JSON 文件writeFileSync( new URL(./${runtime}.json, import.meta.url), new TextEncoder().encode(JSON.stringify(values, null, 2)), { truncate: true }, { truncate: true }, // 注实际代码为单次 { truncate: true } );即在脚本所在目录生成deno.json或node.json其中每个键是函数名如copyFileSync值是各轮速率ops/s的数组。run.mjs 测量框架逐行解读整个脚本不到 70 行核心是bench()函数run.mjs 第 8-19 行let total 5; let current ; const values {}; const runtime typeof Deno ! undefined ? deno : node; function bench(fun, count 100000) { if (total 5) console.log(fun.toString()); const start Date.now(); for (let i 0; i count; i) fun(); const elapsed Date.now() - start; const rate Math.floor(count / (elapsed / 1000)); console.log(time ${elapsed} ms rate ${rate}); values[current] values[current] || []; values[current].push(rate); if (--total) bench(fun, count); else total 5; }关键设计点固定 5 轮total 5每轮跑完后递归再跑一轮5 轮结果全部记入values[current]。第一轮还会把被测闭包源码打印出来作为控制台输出的标识。速率口径rate Math.floor(count / (elapsed / 1000))即每秒完成的操作数ops/s用Date.now()毫秒计时。迭代次数可参数化count默认 100000耗时长的操作可以传小值见下文copyFileSync使用 10000。运行时无关的函数获取getFunction(name)在 Deno 下返回Deno[name]在 Node 下返回fs[name]fs由await import(fs)动态导入这是两个运行时能共用一份脚本的前提。两个 API 名称恰好一一对应copyFileSync、lstatSync等。被测函数清单脚本当前实际启用 6 个基准项前置于文件状态准备run.mjs 第 36-64 行基准项迭代次数前置条件说明copyFileSync(test, test2)10000预先写入 1 MiB 的test文件全量复制大文件单轮耗时最长故降为 1 万次truncateSync(test, 0)100000默认同上截断到 0 长度测元数据/大小变更路径lstatSync(test)100000同上不解析符号链接的 statchownSync(test, uid, gid)100000uid/gid取自上一步lstatSync结果用自身属主回写等价于无变化的 chownchmodSync(test, 0o666)100000同上固定模式 0o666readFileSync(test)100000文件被重新截断写入为 1 KiB读取小文件其中copyFileSync的 1 MiB 源文件用{ truncate: true }保证幂等readFileSync前再次writeFileSync将文件改为 1 KiB避免读取一个刚被反复截断过的 0 字节文件导致速率失真。cwd/chdir两项在脚本中被注释停用第 56-60 行。对比两个运行时的方法就是同一台机器、同一文件系统、相邻时刻分别执行 Deno 与 Node 两条命令再对比各自生成的deno.json与node.json中同名函数的 5 轮速率序列。按 README 规范新增一个基准README 给出的新增示例如下摘自 tests/bench/fs/README.mdconst copyFileSync getFunction(copyFileSync); bench(() copyFileSync(test, test2)); // For functions with side-effects, clean up after bench like so: const removeSync getFunction(removeSync); removeSync(test2);规范要点有两条统一用getFunction(name)取函数而不是直接写Deno.xxx或fs.xxx保证同一基准在两个运行时下都成立并且current变量会同步为该函数名用于 JSON 结果的分组键有副作用的基准要在bench(...)之后清理。bench内部会连跑 5 轮期间文件系统状态会被反复改写如test2被反复覆盖若该状态会污染后续基准项就应像示例那样在基准完成后用removeSync等 API 复位。示例中removeSync也通过getFunction获取——在 Deno 下即Deno.removeSync。新增后只需再次运行两条命令即可让两个运行时都采集到新指标无需改动框架代码。源码纵深Deno.copyFileSync 一次调用发生了什么理解基准测量内容最好看看 Deno 侧copyFileSync的实际路径这也能解释为什么运行命令必须带-AJS 层薄封装ext/fs/30_fs.js 第 144-152 行 中copyFileSync(fromPath, toPath)只做 URL/路径转换pathFromURL随后同步调用op_fs_copy_file_syncOp 层权限检查ext/fs/ops.rs 第 547-571 行 的op_fs_copy_file_sync标注为#[op2(fast, stack_trace)]fast 路径 op可跳过部分检查以提升调用开销小的同步操作性能在真正干活前通过PermissionsContainer::check_open对源路径做读检查、对目标路径做WriteNoFollow检查失败时报错信息会带上Deno.copyFileSync()字样。这就是不带-A时基准会立刻失败的原因——权限检查发生在每次复制之前文件系统抽象层op 层拿到FileSystemRc后调用fs.copy_file_syncext/fs/interface.rs 第 289 行 定义了该 trait 方法默认实现在 ext/fs/std_fs.rs 第 650 行 的copy_file。这里有一个值得注意的防御逻辑先用same_file::is_same_file判断源与目标是否指向同一文件按设备inode 等文件身份比较能识别./、..、符号链接、硬链接若是则报InvalidInput错误而不是像cp那样悄悄清空文件——这解释了为什么基准脚本用test/test2两个不同文件名做复制源和目标。也就是说run.mjs里每一轮copyFileSync的耗时 JS 封装 两次权限检查 fast op 调用 底层文件复制而 Node 的fs.copyFileSync则直接走其内部实现。两者测的是“同语义 API 端到端”的差距这一点在解读deno.json与node.json的数值差异时应当记住。与整套 tests/bench 体系的关系fs 基准属于 tests/bench/ 目录的一部分但运行方式独立Rust 侧基准启动时间、LSP、二进制体积、内存、系统调用计数等由 tests/bench/main.rs 驱动例如 main.rs 第 34-147 行 定义了cold_hello、hello、workers_startup等执行时间基准清单用 hyperfine 采集mean/stddev/min/max等统计量tests/bench/README.md 还给出了按名称过滤的用法cargo bench --bench deno_bench -- bundlefs 基准则是跨运行时Deno vs Node的 JS API 对比产出结构更简单每个函数 5 个速率样本的 JSON 数组。两者互为补充前者度量 Deno 自身随版本变化的绝对指标后者度量与 Node.js 的相对差距。若你在评估某次文件相关改动如ext/fs的权限检查或 op 调度路径对性能的影响跑一遍tests/bench/fs的 Deno/Node 对照是仓库内现成的、无需额外脚手架的验证手段。小结运行在tests/bench/fs/下分别执行deno run -A --unstable run.mjs与node run.mjs得到deno.json/node.json框架bench(fun, count)固定 5 轮、默认 10 万次迭代以 ops/s 记速率getFunction屏蔽运行时差异新增用getFunction取函数、bench(...)注册副作用基准记得清理底层Deno.copyFileSync每次调用都包含权限检查与 fast op 调度ext/fs/30_fs.js、ext/fs/ops.rs、ext/fs/std_fs.rs这是解读其耗时构成时不应忽略的成本项。【免费下载链接】denoA modern runtime for JavaScript and TypeScript.项目地址: https://gitcode.com/GitHub_Trending/de/deno创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门