2015-08-01 13 views
39

Solo per vedere come funziona, ho scritto a mano un modulo asm.js molto breve, che simula l'equazione delle onde 2D usando la matematica a 32 bit interi e gli array tipizzati (Int32Array). Ho tre versioni di esso, tutto il più possibile simili:Perché asm.js peggiora le prestazioni?

  1. ordinaria (cioè leggibile, anche se C-style) JavaScript
  2. Uguale a 1, con asm.js annotazioni aggiunte in modo che passi il validatore, in base a Firefox e altri strumenti
  3. Uguale a 2, tranne che con "usa asm"; direttiva nella parte superiore

Ho lasciato una demo al http://jsfiddle.net/jtiscione/xj0x0qk3/ che consente di passare da un modulo all'altro per vedere gli effetti dell'utilizzo di ciascuno. Tutti e tre funzionano, ma a velocità diverse. Questo è l'hotspot (con annotazioni asm.js):

for (i = 0; ~~i < ~~h; i = (1 + i)|0) { 
    for (j = 0; ~~j < ~~w; j = (1 + j)|0) { 
     if (~~i == 0) { 
      index = (1 + index) | 0; 
      continue; 
     } 
     if (~~(i + 1) == ~~h) { 
      index = (1 + index) | 0; 
      continue; 
     } 
     if (~~j == 0) { 
      index = (1 + index) | 0; 
      continue; 
     } 
     if (~~(j + 1) == ~~w) { 
      index = (1 + index) | 0; 
      continue; 
     } 
     uCen = signedHeap [((u0_offset + index) << 2) >> 2] | 0; 
     uNorth = signedHeap[((u0_offset + index - w) << 2) >> 2] | 0; 
     uSouth = signedHeap[((u0_offset + index + w) << 2) >> 2] | 0; 
     uWest = signedHeap [((u0_offset + index - 1) << 2) >> 2] | 0; 
     uEast = signedHeap [((u0_offset + index + 1) << 2) >> 2] | 0; 
     uxx = (((uWest + uEast) >> 1) - uCen) | 0; 
     uyy = (((uNorth + uSouth) >> 1) - uCen) | 0; 
     vel = signedHeap[((vel_offset + index) << 2) >> 2] | 0; 
     vel = vel + (uxx >> 1) | 0; 
     vel = applyCap(vel) | 0; 
     vel = vel + (uyy >> 1) | 0; 
     vel = applyCap(vel) | 0; 
     force = signedHeap[((force_offset + index) << 2) >> 2] | 0; 
     signedHeap[((u1_offset + index) << 2) >> 2] = applyCap(((applyCap((uCen + vel) | 0) | 0) + force) | 0) | 0; 
     force = force - (force >> forceDampingBitShift) | 0; 
     signedHeap[((force_offset + index) << 2) >> 2] = force; 
     vel = vel - (vel >> velocityDampingBitShift) | 0; 
     signedHeap[((vel_offset + index) << 2) >> 2] = vel; 
     index = (index + 1)|0; 
    } 
} 

La versione "normale JavaScript" è strutturato come sopra, ma senza gli operatori bit per bit che richiede asm.js (ad esempio "x | 0", "~ ~ x "," arr [(x < < 2) >> 2] ", ecc.)

Questi sono i risultati per tutti e tre i moduli sulla mia macchina, utilizzando Firefox (Developer Edition v. 41) e Chrome (versione 44), in millisecondi per iterazione:

  • FIREFOX (versione 41): 20 ms, 35 ms, 60 ms.
  • CHROME (versione 44): 25 ms, 150 ms, 75 ms.

Quindi il comune JavaScript vince in entrambi i browser. La presenza di annotazioni richieste da asm.js peggiora le prestazioni di un fattore 3 in entrambe. Inoltre, la presenza dell '"uso asm"; la direttiva ha un effetto ovvio: aiuta un po 'Firefox, e mette in ginocchio Chrome!

Sembra strano che la semplice aggiunta di operatori bit a bit dovrebbe introdurre un triplice degrado delle prestazioni che non può essere superato dicendo al browser di utilizzare asm.js. Inoltre, perché dire al browser di usare asm.js è di aiuto solo marginalmente in Firefox, e completamente ritorcersi contro Chrome?

+0

Per iniziare, ho eseguito il benchmark ["Massive"] (https://kripken.github.io/Massive/) in Chrome 44 e FF 39 (Win XP, 32 bit), ecco i risultati per [Chrome ] (http://pastebin.com/fZQYzWKs) e [Firefox] (http://pastebin.com/brtZHecb) (copia e copia nel campo "inserisci i dati copiati da un'altra corsa" sulla pagina di benchmark - sì, funziona con l'HTML effettivo). Tranne che per un punto ("poppler-cold-preparation"), Chrome è stato più lento ovunque, nel caso più estremo 24,6 volte più lento di FF. Sembra che Chrome al momento non sia in grado di gestire asm.js in modo ragionevole. – Siguza

+2

solo un'idea, hai "benchmark" le chiamate successive/ripetute, dato che asm userà più tempo durante le fasi di compilazione/opt (suppongo)? – birdspider

+0

@birdspider Significa eseguire il benchmark più volte? No, ho appena preso quello che c'era ... l'interfaccia attuale sembra richiedere una ricarica della pagina per eseguire nuovamente il benchmark, molto probabilmente richiedendo nuovamente la compilazione/ottimizzazione del codice. Ma l'intero benchmark è durato circa 15 minuti per me, quindi penso che il tempo di compilazione non sia un granché. Se Chrome impiega così tanto tempo per essere compilato, mi sconcerta che il codice riesca addirittura a funzionare * affatto *. – Siguza

risposta

9

In realtà asm.js non è stato creato per scrivere codice a mano ma solo come risultato di una compilazione da altre lingue. Per quanto ne so non ci sono strumenti che convalidano il codice asm.js. Hai provato a scrivere il codice in C lang e utilizzare Emscripten per generare il codice asm.js? Sospetto fortemente che il risultato sarebbe abbastanza diverso e ottimizzato per asm.js.

Penso che mescolando vd digitati e non tipizzati si aggiunga solo complessità senza alcun beneficio. Al contrario il codice "asm.js" è più complesso: ho cercato di analizzare i asm.js e le funzioni pianura su jointjs.com/demos/javascript-ast ei risultati sono:

  • la funzione js pianura ha 137 nodi e 746 gettoni
  • la funzione asm.js dispone di 235 nodi e 1252 gettoni

direi che se avete più istruzioni da eseguire in ogni ciclo facilmente sarà più lento.

+3

Anche se hai ragione a riguardo non essendo progettato per essere scritto a mano, [qui] (http://turtlescript.github.cscott.net/asmjs.html) sembra essere un validatore asm.js e OP 'AsmWaveModule' passa il controllo. – Siguza

+0

Inoltre, Firefox stampa questo nella console: 'Codice asm.js compilato con successo (tempo di compilazione totale 1ms, non memorizzato nella cache (troppo piccolo per beneficiare))'. Quando rimuovo un '~~' dal jsfiddle di OP, questo cambia in 'TypeError: errore di tipo asm.js: Disabled by debugger'. Quindi sembrerebbe che asm.js stesso non sia difettoso. – Siguza

+0

Concordo sul fatto che avere il 50% in più di nodi/token dovrebbe rallentarlo, ma questo è un successo sorprendente. Le annotazioni (con "no asm"; incluso) introducono un rallentamento 3X su Firefox e Chrome. L'ho provato su Safari (senza supporto asm.js), tutte e 3 le versioni erano molto lente (non sorprende) ma le annotazioni comportano solo un 50% di rallentamento su Safari. – jtiscione

1

C'è un costo fisso per cambiare i contesti asm.js. Idealmente lo fai una volta ed esegui tutto il codice all'interno della tua app come asm.js. Quindi è possibile controllare la gestione della memoria utilizzando array digitati ed evitare un sacco di garbage collection. Suggerirei di riscrivere il profiler e misurare asm.js all'interno di asm.js - senza cambiare contesto.

+0

Ma in questo caso non ci sono garbage da raccogliere, dal momento che tutte le operazioni vengono eseguite su un heap ArrayBuffer condiviso istanziato all'avvio e accessibile tramite visualizzazioni array digitate (Int32Array e Uint32Array). AFAIK non c'è modo di ospitare un'intera app all'interno di asm.js. Avrai sempre bisogno di codice esterno per istanziare il modulo compilato e invocare i suoi punti di ingresso. – jtiscione

+0

[Ho provato a misurare il tempo da dentro anche iterate] (https://jsfiddle.net/8xk7m6gr/5/), ma non sembra fare la differenza ... o usa 'stdlib. performance.now() 'invoca ancora un altro interruttore di contesto? In tal caso, è persino possibile misurare il tempo effettivo trascorso in una funzione asm.js? – Siguza

+0

Se si passa attraverso la sorgente JS dove inizializza sempre "totalCycles" a 4, e si aumenta a 8 o 12, loop 2X e 3X tante volte attraverso quel codice hotspot sopra. Su Chrome, il rallentamento passava da 22 a 40 a 65 ms per JS ordinario, da 140 a 275 a 400 ms per asm.js e da 70 a 140 a 210 ms per asm.js senza direttiva. Dato che il tempo necessario per l'esecuzione è quasi direttamente proporzionale al tempo che trascorre in quel codice sopra, penso che il sovraccarico dal cambio di contesto sia piuttosto ridotto, almeno in questo caso. – jtiscione

Problemi correlati