2012-04-12 22 views
10

Quindi, sto programmando una simulazione fisica 2D di Javascript. Le prestazioni sono buone, ma sto facendo delle ottimizzazioni per renderlo migliore. Quindi, poiché il programma funziona con molta geometria fisica, eseguo diversi calcoli di Teorema di Pitagora nel programma. In tutto, circa cinque calcoli; insieme, corrono circa un milione di volte al secondo. Quindi, ho pensato che avrebbe aumentato le prestazioni se avessi messo quel semplice codice del teorema di Pitagora in una nuova funzione e l'avessi chiamato; dopotutto, in questo modo il browser ha meno compilazione da fare. Quindi, ho eseguito il codice in Firefox e ottenuto ... un 4000000% aumento nel tempo di esecuzione di tale calcolo.JS: Quanto ci vuole per chiamare una funzione?

Come? È lo stesso codice: Math.sqrt (x * x + y * y), quindi come può aggiungerlo come una funzione rallentarlo? Presumo che la ragione sia che una funzione richiede tempo solo per essere chiamata, senza eseguire il codice, e che aggiungendo che un milione di questi ritardi al secondo la rallenta?

Mi sembra piuttosto allarmante. Ciò vale anche per le funzioni js predefinite? Sembra improbabile, e se è così, come lo evitano?

Il codice utilizzato per andare in questo modo:

function x() 
{ 
    dx=nx-mx; 
    dy=ny-my; 
    d=Math.sqrt(dx*dx+dy*dy); 
    doStuff(... 
} 

Quello che ho provato è stato questo:

function x() 
{ 
    dx=nx-mx; 
    dy=ny-my; 
    d=hypo(dx,dy); 
    doStuff(... 
} 
function hypo(x,y) 
{ 
    return Math.sqrt(x*x+y*y); 
} 

Grazie!

+3

La funzione è definita al di fuori dell'ambito che viene eseguita un milione di volte al secondo? – alex

+0

E non è vero che il browser ha "meno compilazione da fare" perché lo si inserisce in una funzione ... dovrebbe essere pressoché la stessa cosa, in realtà, soprattutto perché la compilazione è una cosa di avvio. Ma @alex ha probabilmente capito il motivo del tuo rallentamento del 400% :) – Ryan

+0

@alex Sì, è definito nella finestra principale. – mindoftea

risposta

7

Le chiamate di funzione sono trascurabili o addirittura ottimizzanti in lingue precompilate che JS non è mai stata. Oltre a ciò, molto dipende dal browser.

Sono la morte di tutte le prestazioni in lingue interpretate che JS è stata principalmente fino a poco tempo fa. La maggior parte dei browser moderni ha compilatori JIT (Just In Time) che è un enorme aggiornamento dagli interpreti JS del passato ma credo che le chiamate di funzione a un altro ambito costano ancora un po 'di overhead perché l'oggetto call di JS deve determinare ciò che viene effettivamente chiamato e ciò significa marcia su e giù per varie catene.

Quindi, come regola generale: se ti interessa IE8 e versioni precedenti e inferiori di Chrome e Firefox, evita il periodo delle chiamate di funzione. Soprattutto all'interno di anelli. Per i browser JIT, mi aspetterei che una funzione definita all'interno dell'altra funzione sarebbe in genere vantaggiosa (ma lo farei ancora testando che questa è una tecnologia nuova per IE9 e relativamente nuova per tutti gli altri).

Un'altra cosa da diffidare. Se una funzione è particolarmente complessa, JIT non può fare nulla per ottimizzarle.

https://groups.google.com/forum/#!msg/closure-compiler-discuss/4B4IcUJ4SUA/OqYWpSklTE4J

Ma la cosa importante da capire è che quando qualcosa è bloccato e chiamato solo all'interno di un contesto, come una funzione all'interno di una funzione, dovrebbe essere facile per un JIT per ottimizzare. Definito al di fuori di una funzione, deve determinare quale definizione di tale funzione viene chiamata esattamente. Potrebbe essere in una funzione esterna. Potrebbe essere globale. Potrebbe essere una proprietà del prototipo del costruttore dell'oggetto della finestra, ecc ... In un linguaggio in cui le funzioni sono di prima classe, nel senso che i loro riferimenti possono essere passati in giro come argomenti allo stesso modo in cui si passano i dati, non si può davvero evitare quel passo al di fuori del tuo contesto attuale.

Quindi prova a definire l'ipo in X per vedere cosa succede.

Un altro paio di suggerimenti generali dall'età interpretato che potrebbe ancora essere utile nel SIC: ''

  • Il operatore come in someObject.property, è un processo che vale la pena memorizzare nella cache. Il costo è oneroso in quanto vi è un processo di ricerca dell'oggetto chiamata associato ogni volta che lo si utilizza. Immagino che Chrome non preservi i risultati di questo processo poiché le modifiche agli oggetti padre o ai prototipi potrebbero alterare ciò che effettivamente fa riferimento al di fuori di un determinato contesto. Nel tuo esempio se x è usato da un loop (probabilmente okay o anche utile se x è definito nella stessa funzione del loop in JITs - omicidio in un interprete), proverei ad assegnare Math.sqrt a una var prima di usarlo in ipo. Avere troppi riferimenti a cose fuori dal contesto della tua funzione attuale potrebbe far sì che alcune JIT decidano che non vale la pena di ottimizzare, ma questa è pura speculazione da parte mia.

  • Quello che segue è probabilmente il modo più veloce per ciclo un array:

//assume a giant array called someArray 
var i = someArray.length; //note the property lookup process being cached here 
//'someArray.reverse()' if original order isimportant 
while(i--){ 
    //now do stuff with someArray[i]; 
} 

nota: blocco di codice non lavora qui per qualche motivo.

Facendolo in questo modo può essere utile perché fondamentalmente trasforma il passo dell'incremento/decremento e il confronto logico nel solo decremento, rimuovendo completamente la necessità di un operatore di confronto sinistra/destra. Si noti che in JS l'operatore di decremento del lato destro indica che io sono passato per essere valutato e quindi decrementato prima di essere utilizzato all'interno del blocco. while(0) restituisce false.

+0

Grazie; una spiegazione molto bella! Definire la funzione ipo all'interno di una funzione più grande richiede troppo tempo e io ne avrei bisogno in altre funzioni, quindi non ne vale la pena per me. Per curiosità, però, ho provato a definirlo all'interno. Questo ha risolto il 97% dei problemi di prestazioni! È ancora gonfiato, ma molto meglio. Sai come le funzioni predefinite lo evitano? Grazie ancora! – mindoftea

+0

Gli oggetti e i metodi del core JS stanno effettivamente operando su un livello molto più basso. Se provi ad avvertire Math.sqrt, ad esempio (senza parents) probabilmente vedrai una funzione con [CODICE NATIVO] o qualcosa all'interno. Questa è in realtà una chiamata che viene prelevata dall'ambiente di run-time precompilato del browser in genere. Può comunque essere utile memorizzare nella cache queste ricerche, anche perché è possibile sostituire quei riferimenti ai metodi definiti in modo nativo con quello che si desidera in JS (non molto intelligente ma consente di farlo). Il che significa che JIT deve ancora tenere conto del processo di ricerca. –

+0

Inoltre, se hai ancora tutto istituito, cercano Hypo definita in linea con 'var = sqrt Math.sqrt' a destra prima di esso e quindi chiamare sqrt invece all'interno ipo. Sono curioso. –

1

Con mia grande sorpresa, la memorizzazione nella cache la ricerca come suggerito da Erik non fa molto per migliorare le prestazioni sul mio browser (Cromo, Linux) ma sembra ferite prestazioni invece: http://jsperf.com/inline-metric-distance

var optimizedDistance = (function() { 
    var sqrt = Math.sqrt; 
    return function (x, y) { return sqrt(x * x + y * y); } 
})(); 

è più lento di

var unoptimizedDistance = function(x, y) { 
    return Math.sqrt(x * x + y * y); 
} 

anche chiamando un alias è più lento

var _sqrt = Math.sqrt; // _sqrt is slower than Math.sqrt! 

Ma poi di nuovo, questa non è una scienza esatta e le misurazioni della vita reale possono ancora variare.

Tuttavia, vorrei usare Math.sqrt.

+0

Interessante. Farò i miei test in un secondo. – mindoftea

+0

OK. Ho testato tutto il codice con e senza il caching di Math. Li ho definiti globali, in modo che le mie diverse funzioni potessero accedervi e quindi eseguirli. Sembra che, tranne in alcuni casi, il caching sia in qualche modo più lento. – mindoftea

Problemi correlati