2015-10-07 6 views
9

Storia di due funzioniPerché utilizzare una funzione del generatore più lenta del riempimento e dell'iterazione di una matrice in questo esempio?

Ho una funzione che riempie un array fino a un valore specificato:

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

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

    return a; 
} 

E una funzione generatore simile che produce invece ogni valore:

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

Test Runner

ho scritto questo test per entrambi questi scenari:

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

Esecuzione dei test di

Poi durante l'esecuzione di questi due in questo modo:

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

ottengo risultati simili a questo:

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 

Nota: sto gestendo questo e test sul nodo v4.1.1 su Windows 8.1. Non sto utilizzando un transpiler e lo sto eseguendo facendo node --harmony generator-test.js.

Domanda

La maggiore utilizzo della memoria con una serie è ovviamente previsto ... ma perché sto sempre ottenendo risultati più rapidi per un array? Cosa sta causando il rallentamento qui? Sta facendo un rendimento solo un'operazione costosa? O forse c'è qualcosa con il metodo che sto facendo per verificare questo?

+1

Ci scusiamo per la domanda stupida ... che lingua è questa? 'function *' non assomiglia alla sintassi di Javascript I in codice, e nemmeno la parola chiave 'const'. –

+4

@ sg.cc scusa, lo so che può essere fonte di confusione. È ES6 javascript, non ES5. Puoi leggere la funzione '' e le altre funzioni qui usate su [MDN] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*). –

+0

In quale ambiente è in esecuzione questo codice? Supporta i generatori in modo nativo o stai usando un transpiler come Babel per generare il codice ES5? –

risposta

4

La risposta terribilmente insoddisfacente è probabilmente questo: La vostra funzione ES5 si basa su caratteristiche che (con le eccezioni di let e const) sono stati nel V8 da quando è stato rilasciato nel 2008 (e presumibilmente per qualche tempo prima, come ho capire che ciò che è diventato V8 è nato come parte del web crawler di Google). I generatori, d'altra parte, sono stati solo in V8 since 2013. Quindi non solo il codice ES5 ha avuto sette anni per essere ottimizzato mentre il codice ES6 ne ha avuti solo due, quasi nessuno (rispetto ai molti milioni di siti che usano il codice proprio come il tuo codice ES5) sta utilizzando i generatori in V8 ancora, il che significa che lì è stata pochissima opportunità di scoprire, o incentivare a implementare, ottimizzazioni per questo.

Se davvero vuole una risposta tecnica sul motivo per cui i generatori sono relativamente lento nel Node.js, probabilmente dovrete tuffarsi nella fonte V8 da soli, o chiedere alla gente che l'ha scritto.

+0

Questa è probabilmente la vera risposta nel tempo. Attualmente ho scoperto, dopo essere stato indirizzato nella direzione giusta da @ Blindman67, che sposta il let al di fuori del ciclo for 'let i; per (i = 0; ... 'causa l'utilizzo di una funzione di generatore per essere più veloce. –

2

Provare a sostituire il "let" nella funzione generatore con una funzione con scope "var". Sembra che il "let" all'interno del ciclo incoraggi un sacco di spese generali. Utilizzare solo let se assolutamente necessario.

Problemi correlati