2014-09-13 15 views
5

Durante la lettura dei documenti, ho trovato una semplice ottimizzazione che migliora notevolmente le prestazioni javascript.Ottimizzazione Javascript con `nuova funzione()`

Codice originale:

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

codice ottimizzato:

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

var parseRow = new Function('columns', 'parser', code); 

trovate qui: https://github.com/felixge/faster-than-c
Perché correre il 20% più veloce?
Credo che rimuova l'istruzione for, ma lo forEach non ha lo stesso costo computazionale?

+0

Si parla di guadagno del 20% con la logica di creazione della funzione stessa? O con ogni chiamata alla funzione parseRow? Se è il secondo caso, suppongo che il guadagno stia accadendo a causa dell'assenza di loop in codice ottimizzato. (Nel primo caso, il ciclo for dovrebbe essere eseguito con ogni chiamata alla funzione parseRow.Inoltre, anche più chiamate alla proprietà columns.length potrebbero essere attribuite alla lentezza del codice originale.Per soli i miei 2 centesimi :)) –

+1

http (per V8) correlati : //www.youtube.com/watch? v = UJPdhx5zTaw –

+1

quando la dimensione di 'columns' è abbastanza grande, il 'codice ottimizzato' non sembra essere più veloce. vedi [jsPerf] (http://jsperf.com/javascript-optimization-with-new-function) – rhgb

risposta

5

La differenza è che si sta utilizzando solo forEach su la funzione ottimizzata. Una volta creata la funzione, non vi è alcun loop all'interno: i nomi e colonna sono hardcoded. Il metodo è quindi eval in una funzione di lavoro, che potrebbe anche essere compilato in codice macchina, depending on the engine. Ciò comporta due miglioramenti di prestazioni:

  1. Rimuovendo il controllo for ciclo condizione (i < columns.length) completamente, non c'è ramificazione, e
  2. Con hardcoding valori column[i].name in più istruzioni, è stato rimosso valutando column[i] e ricerche di column.name in ogni passaggio.

Così, dopo aver chiamato new Function(...) con il codice passato come String, la variabile parseRow ottiene il riferimento alla seguente funzione:

function parseRow(columns, parser) { 
    return { 
     "columnOne": parser.readColumnValue(), 
     "columnTwo": parser.readColumnValue(), 
     "columnThree": parser.readColumnValue(), 
     ... 
    }; 
} 

Si noti che non ci sono loop, ramificazione, o altre ricerche in quel codice, ad eccezione delle chiamate multiple parser.readColumnValue().

Perché è possibile in JavaScript?

Il motivo per cui questo funziona in modo così efficiente in JavaScript è perché il codice sorgente JavaScript in qualsiasi pagina web deve essere comunque interpretato o compilato dal motore JS. Non spedisci la tua pagina web con eseguibili compilati, o anche (in qualche modo) precompilati dal codice (come Java o .NET).Ogni volta che viene caricato un nuovo file .js, il browser lo compila da zero prima di eseguirlo (beh, per essere precisi, nei motori moderni è qualcosa tra l'interpretazione e la compilazione, ovvero JITting).

Ciò significa che la creazione di una funzione di lavoro da una stringa (ovvero la compilazione del codice) durante il runtime non è meno efficiente rispetto alla lettura del codice scritto a mano dal file .js. Confrontalo con un programma C/C++, che è (in tutti i casi ragionevoli) compilato per codice macchina (cioè file eseguibile che è il più vicino possibile alla CPU) before it reaches the customer.

Se si desidera eseguire questa operazione in C++ (una sorta di self-modifying code), è necessario raggruppare un compilatore lungo l'app per creare il codice e il costo di creazione di questa funzione sovrappeserebbe i vantaggi che si otterrebbero quando finalmente lo avvierai. In .NET, ad esempio, non è inusuale per un programma emit methods or even assemblies at run time, che consente di ottenere JIT compilato sul codice della macchina consentendo potenziali miglioramenti delle prestazioni, come quello della domanda.

2

I guadagni delle prestazioni dipendono molto dal motore JavaScript, nonché dai dati, che vengono elaborati. Non conosciamo le circostanze esatte di "20% più veloce" (eccetto che per l'uso di node.js). Potrebbe essere più lento in alcune situazioni. (Modifica: È necessario chiamare la funzione abbastanza spesso per superare i costi di costruzione). Alcuni possibili motivi per i guadagni:

Il codice ottimizzato crea un oggetto letterale. La versione precedente assegna costantemente valori a proprietà non ancora esistenti. Questo ha un costo associato ad esso.

row[columns[i].name] ha tre ricerche, mentre la versione ottimizzata non ne ha, una volta costruita la funzione. E non dimenticare che row[columns[i].name] non esiste ancora, quindi la ricerca è più costosa. columns.length è anche una ricerca.

Problemi correlati