在開發 Node.js 應用程式時,效能瓶頸往往不易察覺。幸運的是,Node.js 提供了內建的 Profiler,可以幫助我們找出程式的效能瓶頸並進行優化。本文將分享我在使用 Node.js Profiler 進行效能優化的經驗。
可以使用 node --inspect-brk
啟動應用程式,並透過 Chrome DevTools 進行分析:
node --inspect-brk somefile.js
執行後打開 Chrome DevTools 點擊 DevTools for Node,進入 Performance 面板,點擊錄製按鈕來進行效能分析。完成後會產生一張 Flame Graph,顯示各個函數的執行時間與呼叫堆疊。
Flame Graph 提供兩種常見檢視方式:
以 eslint-plugin-react
為例,這是一個用於 React 的 ESLint 插件。我們在進行分析時,使用如下指令啟動 Profiler:
node --inspect-brk node_modules/.bin/eslint .
在報告中,我發現名為 isCreateElement
的函數佔用了約 5% 的執行時間,這成為潛在的優化目標:
檢視原始碼後發現,isCreateElement
函數在每次呼叫時都進行了一次昂貴的運算 pragmaUtil.getFromContext(context)
:
module.exports = function isCreateElement(node, context) {
const pragma = pragmaUtil.getFromContext(context);
if (
node.callee &&
node.callee.type === 'MemberExpression' &&
node.callee.property.name === 'createElement' &&
node.callee.object &&
node.callee.object.name === pragma
) {
return true;
}
};
我們將 pragmaUtil.getFromContext(context)
的呼叫直接移到條件判斷的尾端,讓他有機會被 short-circuit 避免執行:
module.exports = function isCreateElement(node, context) {
if (
node.callee &&
node.callee.type === 'MemberExpression' &&
node.callee.property.name === 'createElement' &&
node.callee.object &&
node.callee.object.name === pragmaUtil.getFromContext(context)
) {
return true;
}
};