为什么在这个例子中使用一个生成函数比填充和迭代数组慢? [英] Why is using a generator function slower than filling and iterating an array in this example?

查看:101
本文介绍了为什么在这个例子中使用一个生成函数比填充和迭代数组慢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

两个功能的故事



我有一个函数可以将数组填充到指定的值:

  function getNumberArray(maxValue){
const a = [];

for(let i = 0; i< maxValue; i ++){
a.push(i);
}

返回a;
}

另一个类似的生成器函数,而是产生每个值:

  function * getNumberGenerator(maxValue){
for(let i = 0; i< maxValue; i ++){
yield一世;
}
}

测试运行器 p>

我为这两种情况编写了此测试:

  function runTest (testName,numIterations,funcToTest){
console.log(`运行$ {testName} ...`);
let dummyCalculation;
const startTime = Date.now();
const initialMemory = process.memoryUsage();
const iterator = funcToTest(numIterations);

for(let val of iterator){
dummyCalculation = numIterations - val;
}

const finalMemory = process.memoryUsage();

//注意:formatNumbers可以在这里找到:https://jsfiddle.net/onz1ozjq/
console.log(formatNumbers`总时间:$ {Date.now() - startTime } ms`);
console.log(formatNumbers`Rss:$ {finalMemory.rss - initialMemory.rss}`);
console.log(formatNumbers`Heap Total:$ {finalMemory.heapTotal - initialMemory.heapTotal}`);
console.log(formatNumbers`堆使用:$ {finalMemory.heapUsed - initialMemory.heapUsed}`);
}

运行测试



然后当这样运行这两个时:

  const numIterations = 999999; // 999,999 
console.log(formatNumbers`运行测试与$ {numIterations}迭代... \\\
`);
runTest(Array test,numIterations,getNumberArray);
console.log();
runTest(Generator test,numIterations,getNumberGenerator);

我的结果与此类似:

 运行999,999次迭代测试... 

运行数组测试...
总时间:105ms
Rss:31,645,696
堆总数:31,386,624
堆二手:27,774,632

运行函数生成器测试...
总时间:160ms
Rss:2,818,048
堆总数: 0
堆使用:1,836,616

注意:我在节点v4.1.1上运行这些测试在Windows 8.1上。我没有使用透析器,我通过执行节点--harmony generator-test.js 来运行它。



问题



显然预期数组的内存使用量会增加,但为什么我会持续获得更快的数组结果?这是什么导致这里的放缓?做一个收益只是一个昂贵的操作?或者也许有什么方法我正在做的检查这个?

解决方案

非常不满意的答案是可能:您的ES5功能依赖于(除了 let const 之外的功能)在V8自2008年发布以来(大概是在一段时间之前,据了解,V8已经成为Google网页爬虫的一部分)。另一方面,发电机仅在V8中自2013年以来。因此,ES5代码不仅有7年的时间,而ES6代码只有两个,几乎没有人(相比之下,数百万个使用代码的网站和ES5代码相比)在V8中正在使用生成器,这意味着已经很少有机会发现或激励实施优化。



如果您真的想要一个技术答案,为什么生成器在Node.js中比较慢,你可能不得不自己潜入V8源码,或者询问写入它的人。


A Tale of Two Functions

I have one function that fills an array up to a specified value:

function getNumberArray(maxValue) {
    const a = [];

    for (let i = 0; i < maxValue; i++) {
        a.push(i);
    }

    return a;
}

And a similar generator function that instead yields each value:

function* getNumberGenerator(maxValue) {
    for (let i = 0; i < maxValue; i++) {
        yield i;
    }
}

Test Runner

I've written this test for both these scenarios:

function runTest(testName, numIterations, funcToTest) {
    console.log(`Running ${testName}...`);
    let dummyCalculation;
    const startTime = Date.now();
    const initialMemory = process.memoryUsage();
    const iterator = funcToTest(numIterations);

    for (let val of iterator) {
        dummyCalculation = numIterations - val;
    }

    const finalMemory = process.memoryUsage();

    // note: formatNumbers can be found here: https://jsfiddle.net/onz1ozjq/
    console.log(formatNumbers `Total time: ${Date.now() - startTime}ms`);
    console.log(formatNumbers `Rss:        ${finalMemory.rss - initialMemory.rss}`);
    console.log(formatNumbers `Heap Total: ${finalMemory.heapTotal - initialMemory.heapTotal}`);
    console.log(formatNumbers `Heap Used:  ${finalMemory.heapUsed - initialMemory.heapUsed}`);
}

Running the Tests

Then when running these two like so:

const numIterations = 999999; // 999,999
console.log(formatNumbers `Running tests with ${numIterations} iterations...\n`);
runTest("Array test", numIterations, getNumberArray);
console.log("");
runTest("Generator test", numIterations, getNumberGenerator);

I get results similar to this:

Running tests with 999,999 iterations...

Running Array test...
Total time: 105ms
Rss:        31,645,696
Heap Total: 31,386,624
Heap Used:  27,774,632

Running Function generator test...
Total time: 160ms
Rss:        2,818,048
Heap Total: 0
Heap Used:  1,836,616

Note: I am running these tests on node v4.1.1 on Windows 8.1. I am not using a transpiler and I'm running it by doing node --harmony generator-test.js.

Question

The increased memory usage with an array is obviously expected... but why am I consistently getting faster results for an array? What's causing the slowdown here? Is doing a yield just an expensive operation? Or maybe there's something up with the method I'm doing to check this?

解决方案

The terribly unsatisfying answer is probably this: Your ES5 function relies on features that (with the exceptions of let and const) have been in V8 since it was released in 2008 (and presumably for some time before, as I understand that what became V8 originated as part of Google's web crawler). Generators, on the other hand, have only been in V8 since 2013. So not only has the ES5 code had seven years to be optimized while the ES6 code has had only two, almost nobody (compared to the many millions of sites using code just like your ES5 code) is using generators in V8 yet, which means there has been very little opportunity to discover, or incentive to implement, optimizations for it.

If you really want a technical answer as to why generators are comparatively slow in Node.js, you'll probably have to dive into the V8 source yourself, or ask the people who wrote it.

这篇关于为什么在这个例子中使用一个生成函数比填充和迭代数组慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆