23

Sto provando a migliorare le prestazioni di uno script quando eseguito in un web worker. È progettato per analizzare file di testo di grandi dimensioni nel browser senza crash. Tutto funziona piuttosto bene, ma noto una grave differenza di prestazioni per file di grandi dimensioni quando si utilizza un web worker.Perché le prestazioni del web worker diminuiscono drasticamente dopo 30 secondi?

Quindi ho condotto un semplice esperimento. Ho eseguito lo script sullo stesso input due volte. La prima esecuzione ha eseguito lo script nel thread principale della pagina (nessun web worker). Naturalmente, questo fa sì che la pagina si blocchi e non risponda. Per la seconda esecuzione, ho eseguito lo script in un web worker.

Per piccoli file in questo esperimento (< ~ 100 MB), la differenza di prestazioni è trascurabile. Tuttavia, il file di grandi dimensioni, l'analisi richiede circa 20 volte più a lungo il thread di lavoro: si prevede

Performance of both scenarios on same graph

La linea blu. Deve solo prendere circa 11 secondi per analizzare il file, e le prestazioni sono abbastanza costante:

Performance of script without web worker

La linea rossa è la performance all'interno del lavoratore web. E 'molto più sorprendente:

Performance of script in web worker

La linea frastagliata per i primi 30 secondi è normale (la Jag è causata dal leggero ritardo nell'invio dei risultati al thread principale dopo ogni blocco del file viene analizzato). Tuttavia, l'analisi rallenta piuttosto bruscamente a 30 secondi. (Si noti che sto usando sempre e solo un singolo lavoratore web per il lavoro;. Mai più di un thread di lavoro alla volta)

Ho confermato che il ritardo è non a mandare i risultati alla principale thread con postMessage(). Il rallentamento è in the tight loop del parser, che è completamente sincrono. Per ragioni che non riesco a spiegare, quel loop è drasticamente rallentato e diventa più lento con il tempo dopo 30 secondi.

Ma questo succede solo in un web worker. L'esecuzione dello stesso codice nel thread principale, come hai visto sopra, è molto semplice e veloce.

Perché sta succedendo? Cosa posso fare per migliorare le prestazioni? (Non mi aspetto che nessuno capisca a fondo tutte le oltre 1.200 linee di codice in quel file. Se lo fai, è fantastico, ma ho la sensazione che questo sia più correlato al web worker che al mio codice, dal momento che funziona bene nel main thread.)

Sistema: eseguo Chrome 35 su Mac OS 10.9.4 con 16 GB di memoria; Intel Core i7 quad-core da 2,7 GHz con cache L2 da 256 KB (per core) e L3 Cache da 6 MB. I blocchi di file hanno una dimensione di circa 10 MB.

Update: appena provato su Firefox 30 e ha fatto non esperienza lo stesso rallentamento in un thread di lavoro (ma era più lento di Chrome quando viene eseguito nel thread principale). Tuttavia, provare lo stesso esperimento con un file ancora più grande (circa 1 GB) ha prodotto un rallentamento significativo dopo circa 35-40 secondi (sembra).

+0

Sto vedendo la stessa cosa con un semplice ciclo i = i + 1, stampando un messaggio ogni 1 milione di iterazioni. Il rallentamento inizia dopo circa 20 secondi in Chrome e 12 secondi in Firefox. Cos'è questo? –

risposta

12

Tyler Ault suggested one possibility on Google+ che si è rivelato molto utile.

ha ipotizzato che l'uso FileReaderSync nel thread di lavoro (invece della pianura ol' async FileReader) non forniva l'occasione per la raccolta dei rifiuti avvenga.

Cambiare il thread di lavoro di utilizzare FileReader in modo asincrono (che sembra intuitivamente come un passo indietro prestazioni ) ha accelerato il processo di back up a soli 37 secondi, proprio dove mi aspettavo che fosse.

non ho sentito indietro da Tyler ancora e io non sono del tutto sicuro di aver capito il motivo per cui la raccolta dei rifiuti dovrebbe essere il colpevole, ma qualcosa di FileReaderSync era drasticamente rallentare il codice.

+0

Quindi sto avendo lo stesso problema ma la mia garbage collection non sta accadendo a causa di una catena di promesse che non consente il recupero della memoria dell'heap. Usando le istantanee posso vedere che il fileReader rallenta a destra quando l'heap si espande e poi striscia lungo l'attesa del garbage collector per liberare più spazio. – James

+0

Forse la garbage collection può accadere solo una volta che si ritorna al ciclo degli eventi. –

2

Che hardware stai utilizzando? È possibile che si stiano verificando problemi di caching della cache con la CPU. Ad esempio se la cache della CPU è 1MB per core (solo un esempio) e si inizia a provare a lavorare con i dati che sostituiscono continuamente la cache (mancano le cache), si soffriranno dei rallentamenti - questo è abbastanza comune con i sistemi MT. Questo è comune anche nei trasferimenti di I/O. Anche questi sistemi tendono ad avere alcuni overhead del sistema operativo anche per i contesti del thread. Quindi, se vengono generati molti thread, è possibile che si stia trascorrendo più tempo nella gestione dei contesti rispetto al thread che sta 'facendo il lavoro'. Non ho ancora guardato il tuo codice, quindi potrei essere lontano - ma la mia ipotesi è sul problema di memoria solo a causa di ciò che sta facendo la tua applicazione. :)

Oh. Come risolvere. Prova a rendere i blocchi di esecuzione piccoli pezzi singoli che corrispondono all'hardware. Riduci al minimo la quantità di thread in uso in una sola volta: prova a mantenere 2-3 volte la quantità di core che hai nell'hardware (dipende in realtà da che tipo di hw hai). Spero possa aiutare.

+0

Grazie per queste idee. Aggiunte le specifiche di sistema alla mia domanda. Ridurre la dimensione del blocco è un'idea interessante; L'ho provato con blocchi da 2 MB invece di blocchi da 10 MB e ho notato comunque una riduzione significativa della velocità per gli ultimi milioni di righe (circa 30 secondi in più). E funziona sempre e solo con un web worker (chiarito anche nella mia domanda). Quindi sembra che abbiamo ancora bisogno di trovare una spiegazione, sfortunatamente. – Matt

Problemi correlati