使用`new Function()`进行Javascript优化

ovi*_*ovi 5 javascript optimization eval

在阅读文档时,我发现了一个简单的优化,可以极大地提高javascript的性能.

原始代码:

function parseRow(columns, parser) {
  var row = {};
  for (var i = 0; i < columns.length; i++) {
    row[columns[i].name] = parser.readColumnValue();
  }
}
Run Code Online (Sandbox Code Playgroud)

优化代码:

var code = 'return {\n';
columns.forEach(function(column) {
  code += '"' + column.name + '":' + 'parser.readColumnValue(),\n';
});
code += '};\n';

var parseRow = new Function('columns', 'parser', code);
Run Code Online (Sandbox Code Playgroud)

在这里找到:https ://github.com/felixge/faster-than-c
为什么它运行速度提高20%?
我相信它删除了for声明,但是没有forEach相同的计算成本?

Gro*_*roo 5

不同之处在于您仅forEach用于构建优化函数.创建函数后,内部没有任何循环:循环展开,列名称是硬编码的.然后将该方法eval编入工作函数,甚至可以将其编译成机器代码,具体取决于引擎.这导致两项性能改进:

  1. 通过完全删除for循环条件check(i < columns.length),没有分支,和
  2. 通过将值硬编码column[i].name到多个语句中,您在每个步骤中删除了评估column[i]和查找column.name.

因此,在new Function(...)使用作为a传递的代码调用之后String,您的parseRow变量将获得对以下函数的引用:

function parseRow(columns, parser) {
    return {
        "columnOne": parser.readColumnValue(),
        "columnTwo": parser.readColumnValue(),
        "columnThree": parser.readColumnValue(),
        ...
    };
}
Run Code Online (Sandbox Code Playgroud)

请注意,除了多个parser.readColumnValue()调用之外,该代码中没有任何循环,分支或其他查找.

为什么这在JavaScript中可行?

之所以在JavaScript中如此高效地运行,是因为任何网页中的JavaScript源代码都需要由JS引擎解释或编译.您不会使用已编译的可执行文件,甚至(某种程度上)预编译的字节码(如Java或.NET)发布您的网页.每次.js加载一个新文件时,你的浏览器都会在运行之前从头开始编译它(确切地说,在现代引擎中它是解释和编译之间的东西,即JITting).

这意味着在运行时从字符串创建工作函数(即编译代码)并不比从.js文件读取手写代码有效.将它与C/C++程序进行比较,C/C++程序(在所有合理的情况下)在到达客户之前编译为机器代码(即可以接近CPU的可执行文件).

如果你想用C++(一种自修改代码)来做这件事,你必须在应用程序中捆绑一个编译器来构建代码,而构建这个函数的成本会超过你获得的好处.终于会开始了.例如,在.NET中,程序在运行时发出方法甚至程序集也是不寻常的,然后将JIT编译为机器代码,从而允许潜在的性能改进,例如您的问题.