2015-10-07 9 views
9

Opowieść dwóch funkcjiDlaczego używa się funkcji generatora wolniejszej niż wypełnianie i iterowanie tablicy w tym przykładzie?

mam jedną funkcję, która wypełnia tablicę do określonej wartości:

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

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

    return a; 
} 

i podobną funkcję generatora, że ​​zamiast Plony każdą z wartości:

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

Test Runner

pisałem ten test dla obu tych scenariuszy:

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}`); 
} 

uruchomione testy

Następnie po uruchomieniu tych dwóch tak:

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); 

uzyskać wyniki podobne do tego:

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 

Uwaga: używam tych e testy na węźle v4.1.1 w Windows 8.1. Nie używam transpilera i uruchamiam go wykonując node --harmony generator-test.js.

Pytanie

Zwiększone zużycie pamięci jest oczywiście z oczekiwanym tablicy ... ale dlaczego ja konsekwentnie coraz szybszych wyników na tablicy? Co powoduje spowolnienie tutaj? Czy wydajność jest tylko kosztowną operacją? A może jest coś z metodą, którą robię, aby to sprawdzić?

+1

Przepraszamy za głupie pytanie ... jaki to jest język? 'function *' nie wygląda jak składnia kodu JavaScript I, ani nie jest słowem kluczowym 'const'. –

+4

@ sg.cc przepraszam, wiem, że może być mylące. To ES6 javascript-nie ES5. Możesz przeczytać o 'funkcji *' i innych funkcjach używanych tutaj [MDN] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*). –

+0

W jakim środowisku korzystasz z tego kodu? Czy wspiera generatory natywnie, czy używasz transpilatora takiego jak Babel do generowania kodu ES5? –

Odpowiedz

4

Odpowiedź strasznie niezadowalająca jest prawdopodobnie to: Twoja funkcja ES5 opiera się na cechach, które (z wyjątkami let i const) zostały w V8, ponieważ został wydany w 2008 roku (i zapewne przez jakiś czas przed, jak zrozum, że to, co stało się V8, powstało w ramach robota sieciowego Google'a). Z drugiej strony generatory były tylko w wersji V8 since 2013. Zatem nie tylko kod ES5 miał siedem lat na zoptymalizowanie, podczas gdy kod ES6 miał tylko dwa, prawie nikt (w porównaniu do wielu milionów stron używających kodu, podobnie jak twój kod ES5) używa generatorów w wersji V8, co oznacza, że było bardzo mało okazji do odkrycia lub zachęty do wdrożenia, optymalizacji dla niego.

Jeśli chcesz uzyskać odpowiedź techniczną na pytanie, dlaczego generatory są stosunkowo powolne w Node.js, prawdopodobnie będziesz musiał sam zagłębić się w źródło V8 lub zapytać osoby, które je napisały.

+0

Jest to prawdopodobnie prawdziwa odpowiedź w czasie. Obecnie odkryłem, po wskazaniu we właściwym kierunku przez @ Blindman67, że przenoszenie let poza pętli for 'let i; for (i = 0; ... 'powoduje, że używanie funkcji generatora jest szybsze. –

2

Spróbuj zastąpić "let" w funkcji generatora funkcją o nazwie "var". Wygląda na to, że "niech" w pętli wiąże się z dużym obciążeniem. Używaj tylko let, jeśli koniecznie musisz.

Powiązane problemy