如何优化NodeJS脚本运行场景下的进程启动速度?
Node.js 启动速度优化方案
可根据自身使用场景选择以下方案,所有方案均不需要更换JS引擎,也无需安装第三方C/C++扩展:
- 内置启动参数裁剪
类比Ruby禁用gems的优化思路,Node启动时可通过参数关闭默认加载的非必要组件,直接降低初始化开销,常用有效参数如下:
如果不需要调试、诊断能力,还可加上# 空脚本测试耗时可从默认的0.05s降到0.022s左右 node --no-warnings --no-global-search-paths --no-enable-source-maps -e 'console.log("hi")'--no-inspect进一步压缩耗时;调用node时尽量写二进制文件的绝对路径,还能省掉Shell遍历PATH查找程序的几毫秒开销。 - 自定义V8启动快照
Node 18及以上版本支持自定义启动快照,可把常用的内置模块(比如fs、path等文件处理依赖)提前预编译到快照blob文件里,启动时不需要重新解析、编译这些内置模块的代码,生成快照的命令如下:
后续跑脚本时指定快照文件启动即可,空脚本启动耗时可降到0.015s左右,和禁用gems的Ruby速度相当:# 提前把常用的fs、path、os模块预编译进快照 node --snapshot-blob node.base.snapshot --build-snapshot -e 'require("node:fs"); require("node:path"); require("node:os")'node --snapshot-blob node.base.snapshot your-script.js - 常驻进程复用方案
如果是在Bash循环里上千次调用Node逻辑、或者开发基于Node的命令行提示符,完全没必要每次都启动新的Node进程,可以写一个极简的常驻Node后台进程,通过标准输入输出或者本地Unix套接字接收要执行的任务、返回结果,只做一次V8和Node环境初始化,后续单次调用开销不到1ms,完全没有启动等待。
最简常驻进程示例:
启动后台常驻进程后,Bash中只要把要执行的JS代码写入进程的stdin、读取stdout的返回值即可,适合高频调用场景。// daemon.js import { createInterface } from 'node:readline/promises' import { stdin, stdout } from 'node:process' const rl = createInterface({ input: stdin }) // 提前加载常用模块 import fs from 'node:fs' import path from 'node:path' for await (const task of rl) { try { const res = eval(task) stdout.write(JSON.stringify({ok: true, data: res}) + '\n') } catch (err) { stdout.write(JSON.stringify({ok: false, err: err.message}) + '\n') } } - 编译裁剪版Node二进制
如果以上方案还不能满足需求,可以在编译Node源码时直接裁剪掉不需要的内置模块(比如HTTP、Net、Crypto、Inspector、诊断相关组件),只保留fs、path等需要的基础文件处理能力,编译出来的二进制体积可从默认的40MB+压缩到3-5MB,空脚本启动耗时可压到0.01s以内,和Lua、Perl的启动速度接近,全程不需要依赖任何第三方扩展。
额外注意:跑脚本时不要用ts-node、tsx这类带运行时转译的启动器,这类工具会额外增加30-100ms的启动开销,如果写TypeScript脚本,提前编译成纯JS再运行即可;写脚本时不要在入口顶层全量加载所有依赖,非首屏用到的模块可以延迟到实际调用时再require,进一步降低初始化耗时。
内容的提问来源于stack exchange,提问作者15 Volts
相关产品推荐
相关产品推荐

