在阅读文档时,我发现了一个可以大大提高 javascript 性能的简单优化。
原代码:
function parseRow(columns, parser) {
var row = {};
for (var i = 0; i < columns.length; i++) {
row[columns[i].name] = parser.readColumnValue();
}
}
优化代码:
var code = 'return {\n';
columns.forEach(function(column) {
code += '"' + column.name + '":' + 'parser.readColumnValue(),\n';
});
code += '};\n';
var parseRow = new Function('columns', 'parser', code);
在这里找到:https://github.com/felixge/faster-than-c
为什么它的运行速度提高了 20%?
我相信它删除了 for
语句,但是 forEach
是否具有相同的计算成本?
最佳答案
区别在于您只使用了 forEach
构建优化函数。创建函数后,内部没有任何循环:loop is unrolled和列名是 hardcoded .然后方法是eval
编辑成一个工作函数,甚至可以编译成机器代码,depending on the engine .这会带来两项性能改进:
- 删除
for
完全循环条件检查(i < columns.length
),没有分支, - 通过对
column[i].name
的值进行硬编码在多个语句中,您删除了评估column[i]
并查找column.name
在每一步。
所以在调用new Function(...)
之后代码作为 String
传递, 你的 parseRow
变量获取对以下函数的引用:
function parseRow(columns, parser) {
return {
"columnOne": parser.readColumnValue(),
"columnTwo": parser.readColumnValue(),
"columnThree": parser.readColumnValue(),
...
};
}
请注意,除了多个 parser.readColumnValue()
之外,该代码中没有任何循环、分支或其他查找。电话。
为什么这在 JavaScript 中可行?
这在 JavaScript 中如此有效的原因是因为任何网页中的 JavaScript 源代码无论如何都需要由 JS 引擎解释或编译。您不会在网页上发布已编译的可执行文件,甚至(某种程度上)预编译的字节码(如 Java 或 .NET)。每次都是新的 .js
文件加载后,您的浏览器会在运行之前从头开始编译它(好吧,准确地说,在现代引擎中,它介于解释和编译之间,即 JITting )。
这意味着在运行时从字符串创建工作函数(即编译代码)的效率不亚于从 .js 文件中读取手写代码。将其与 C/C++ 程序进行比较,C/C++ 程序(在所有合理的情况下)被编译为机器代码(即尽可能接近 CPU 的可执行文件)before it reaches the customer .
如果您想在 C++ 中执行此操作(类似于 self-modifying code),则必须在您的应用程序中捆绑一个编译器来构建代码,构建此函数的成本会超过您将获得的 yield 当你最终开始它时。例如,在 .NET 中,程序 emit methods or even assemblies at run time 也很常见。 ,然后将 JIT 编译为机器代码,从而实现潜在的性能改进,例如您问题中的那个。
关于使用 `new Function()` 优化 Javascript,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25823024/