2012-09-30 12 views
10

Spesso ho argomenti opzionali nelle funzioni, ma alcuni test mostrano un enorme successo di prestazioni per loro in Firefox e Safari (70-95%). Stranamente, se passo il valore letterale non definito allora non c'è penalità. Cosa potrebbe accadere qui? Non avrei pensato che si trattasse di un problema a catena di portata in quanto sono intrinsecamente locali alla funzione. Devo iniziare a passare undefined in ogni argomento facoltativo?Penalità delle prestazioni per argomenti non definiti

jsPerf: http://jsperf.com/function-undefined-args/2

+5

@MattWhipple - Questo è un post interessante, ma cosa ha a che fare con la domanda dell'OP? (Bella domanda, a proposito, @robC.) –

+0

Questa è una scoperta interessante.Prima di cambiare tutto il codice per riempire tutti gli argomenti, verificherei se esiste una segnalazione di bug aperta per quelle implementazioni JS e, se non la prima. –

+0

Sospetto fortemente che il runtime stia integrando le funzioni quando c'è un conteggio dei parametri, ma eseguendo la funzione come una chiamata di funzione effettiva altrimenti. Il fatto che nulla faccia alcun uso del valore restituito dalla funzione può significare che il runtime non fa assolutamente nulla * quando i parametri sono completamente forniti. – Pointy

risposta

5

Per una funzione come questa:

function threeArgs(x, y, z) { 
    return x + y + z; 
} 

che si chiama in questo modo:

threeArgs(1, 2, 3); 

l'ottimizzatore è libero di fare la scelta di generare alcun codice a tutti. È abbastanza facile per esso determinare che non ci sono effetti collaterali, perché la funzione fa semplicemente riferimento ai suoi valori di parametro e restituisce il risultato di un'espressione semplice. Poiché il valore di ritorno è ignorato, non c'è motivo per il runtime di fare qualsiasi cosa.

Oltre a ciò, se il codice fosse:

something += threeArgs(1, 2, 3); 

l'ottimizzatore potrebbe decidere di generare il codice più o meno equivalente a:

something += 6; 

Perché? Perché la chiamata è stata effettuata con costanti numeriche e può tranquillamente piegare quelle al momento della generazione del codice. Potrebbe essere prudente, perché i numeri sono strani, ma qui sono tutti interi, quindi potrebbe benissimo farlo. Anche se così non fosse, si potrebbe tranquillamente inline la funzione:

something += 1 + 2 + 3; 

Quando c'è un parametro mancante, tuttavia, può essere che l'ottimizzatori bail out e generare una vera e propria chiamata di funzione. Per una funzione così semplice, il sovraccarico della chiamata di funzione potrebbe facilmente spiegare una grande differenza di prestazioni.

Utilizzando le variabili anziché le costanti in un test e utilizzando effettivamente il valore restituito della funzione, è possibile "confondere" l'ottimizzatore e impedirgli di saltare la chiamata o pre-calcolare il risultato, ma si può t tenerlo da inlining. Continuo a pensare che il tuo risultato sia interessante per questo motivo: espone il fatto che (ad oggi comunque) quegli ottimizzatori sono sensibili al modo in cui vengono invocate le funzioni.

+2

Questa sembra una spiegazione plausibile, ma come facciamo a sapere che questo è davvero ciò che sta accadendo? – jfriend00

+0

I risultati per FF e l'ultima versione di Chrome sono così avanti rispetto al campo che possono davvero essere solo le ottimizzazioni. La chiamata alla funzione che comporta una penalità è in realtà intorno alle stesse prestazioni di altri browser non ottimizzati. – robC

+0

"Quando manca un parametro, tuttavia, potrebbe essere che gli ottimizzatori si salvino" - potresti spiegare perché è così? Se manca un argomento, è garantito che sia indefinito, quindi a me sembra che sia solo in linea '1 + 2 + indefinito'. – pimvdb

2

Penso che quello che potrebbe spiegare la differenza di prestazioni è il modo in cui gli argomenti vengono passati a un oggetto funzione: tramite l'oggetto arguments. Quando non si passa alcun argomento, JS inizierà la scansione dell'oggetto argomenti per uno qualsiasi degli argomenti specificati, quando quelli non sono definiti, la catena di prototipi arguments verrà scansionata, fino a Object.prototype. Se a tutti manca la proprietà desiderata, JS restituirà undefined. Considerando che, passando definito in modo esplicito, lo imposta come una proprietà direttamente sugli argomenti oggetto:

function foo(arg) 
{ 
    console.log(arguments.hasOwnProperty('0')); 
} 
foo();//false' 
foo('bar');//true 
foo(undefined);//true 

ho capito che è il motivo per cui passando indefinito tende esplicitamente di essere più veloce.

+4

Questa è un'idea interessante, ma mi sembra che dal momento che il simbolo "arg" sia dichiarato nella lista dei parametri formali sarà * sempre * un riferimento locale. – Pointy

+0

Forse, ma cambiando la funzione in 'console.log (arg === argomenti [0]);' e chiamando 'foo (new Date())' registra true, quindi entrambi fanno riferimento alla stessa identica cosa. Direi che args è un riferimento locale a una proprietà dell'oggetto locale 'arguments'. Scope Scope da parte, gli argomenti del prototipo sono ancora validi, non è vero? –

+2

Oh sì, sono d'accordo su questo - l'oggetto 'arguments' è bizzarro. Non sono sicuro di come influirebbe su questo, è ciò che intendevo. In altre parole, poiché "arg" è un parametro formale, il suo valore è * sempre * il valore di 'arguments [0]', che sia 'undefined' o no. – Pointy

Problemi correlati