2010-03-15 14 views
16

In un'applicazione Android, desidero utilizzare la classe Scanner per leggere un elenco di float da un file di testo (è un elenco di coordinate del vertice per OpenGL). codice esatto è:java.util.Scanner è lento?

Scanner in = new Scanner(new BufferedInputStream(getAssets().open("vertexes.off"))); 
final float[] vertexes = new float[nrVertexes]; 
for(int i=0;i<nrVertexFloats;i++){ 
    vertexes[i] = in.nextFloat(); 
} 

Sembra, tuttavia, che questo è incredibilmente lento (ci sono voluti 30 minuti per leggere 10.000 carri!) - come testato sul 2.1 emulatore. Cosa sta succedendo? Non ricordo che Scanner sia così lento quando l'ho usato sul PC (a dire il vero non ho mai letto più di 100 valori prima). O è qualcos'altro, come leggere da un flusso di input di asset?

Grazie per l'aiuto!

+3

Vorrei suggerire di profilarlo: http://developer.android.com/intl/zh-TW/guide/developing/tools/traceview.html – yanchenko

+3

Grazie per il suggerimento. L'ho profilato (per 100 float) e sembra che le chiamate a nextFloat richiedano tutto il tempo. A causa di BufferedInputStream vengono effettuate solo 2 chiamate da leggere dall'input e richiedono pochissimo tempo (35 ms/chiamata). Comunque le chiamate a nextFloat richiedono 435ms/chiamata che è enorme. Osservando le chiamate dei bambini sembra che le chiamate all'interno di NumberFormat e Pattern siano gli assassini (molte allocazioni di memoria). Proverò qualche altro metodo di analisi e riferirò. –

+1

Sembra che lo scanner sia davvero MOLTO lento sul dispositivo/emulatore! Potrebbe essere a causa dell'enorme numero di allocazioni di memoria. Sull'emulatore ci vogliono 30 minuti per leggere 10.000 galleggianti. Sul PC ci vuole 1 secondo per leggere 20.000 float (con Scanner). Come soluzione ho trovato il seguente che funziona molto bene: prima analizzo il mio file di input sul PC e lo trasformo in dati binari, quindi lo leggo sul dispositivo byte per byte (bufferizzato) e ricostruisco i numeri. Questo è MOLTO più veloce. Ci vogliono 1,5 secondi per leggere 20.000 galleggianti. Dico che è un enorme miglioramento da 1 ora :) Grazie per tutto l'aiuto! –

risposta

8

Non so su Android, ma almeno in JavaSE, Scanner è lento.

Internamente, Scanner esegue la conversione UTF-8, che è inutile in un file con float.

Dato che tutto ciò che si vuole fare è leggere i float da un file, si dovrebbe usare il pacchetto java.io.

I ragazzi su SPOJ sono in difficoltà con la velocità I/O. È un sito di concorso di programmazione polacco con problemi molto difficili. La loro differenza è che accettano una più ampia gamma di linguaggi di programmazione rispetto ad altri siti, e in molti dei loro problemi, l'input è così grande che se non si scrive I/O efficiente, il programma farà scoppiare il limite di tempo.

Controllare i rispettivi forum, ad esempio here, per un'idea di un parser personalizzato.

Ovviamente, suggerisco contro scrivendo il proprio parser float, ma se avete bisogno di velocità, è ancora una soluzione.

+1

Anche se Scanner è lento, 30 minuti per 10.000 galleggianti non è affatto vicino a un tempo ragionevole, anche se Scanner ha fatto 10 conversioni inutili di charset. –

+2

Sembra che lo scanner sia davvero MOLTO lento sul dispositivo/emulatore! Potrebbe essere a causa dell'enorme numero di allocazioni di memoria. Sull'emulatore ci vogliono 30 minuti per leggere 10.000 galleggianti. Sul PC ci vuole 1 secondo per leggere 20.000 float (con Scanner). Come soluzione ho trovato il seguente che funziona molto bene: prima analizzo il mio file di input sul PC e lo trasformo in dati binari, quindi l'ho letto sul dispositivo byte per byte (bufferizzato) e ricostruisco i numeri. Questo è MOLTO più veloce. Ci vogliono 1,5 secondi per leggere 20.000 galleggianti. Dico che è un enorme miglioramento da 1 ora :) Grazie per tutto l'aiuto! –

0

Sì, non vedo nulla di simile. Riesco a leggere circa 10M in questo modo in 4 secondi sul desktop, ma non può essere così diverso.

Sto provando a pensare ad altre spiegazioni: forse sta bloccando la lettura del flusso di input da getAssets()? Potrei provare a leggere pienamente quella risorsa, a cronometrare quello, e poi a vedere quanto tempo è necessario per eseguire la scansione.

+0

È diverso. Dimentichi che anche se si tratta dello stesso codice Java, Android ha la sua implementazione di Runtime Environment. Esistono diverse implementazioni per tutto, inclusi elementi di base come la codifica di charset e l'allocazione di memoria. Le nuove implementazioni sono possibili meglio, ma questo è stato il caso quando ho postato. –

0

Scanner può essere parte del problema, ma è necessario profilo il codice per sapere. Le alternative potrebbero essere più veloci. Ecco un semplice benchmark confrontando Scanner e StreamTokenizer.

0

Ho avuto esattamente lo stesso problema. Ci sono voluti 10 minuti per leggere il mio file di 18 KB. Alla fine ho scritto un'applicazione desktop che converte i numeri leggibili da umani in un formato leggibile dalla macchina, utilizzando DataOutputStream.

Il risultato è stato sorprendente.

Btw, quando l'ho tracciato, la maggior parte delle chiamate al metodo Scanner comporta espressioni regolari, la cui implementazione è fornita dai pacchetti com.ibm.icu.** (progetto IBM ICU). È davvero eccessivo.

The same goes for String.format. Evitalo in Android!

21

Come altri utenti hanno affermato, è più efficiente includere i dati in un formato binario.Tuttavia, per una soluzione rapida che ho trovato che la sostituzione:

scanner.nextFloat(); 

con

Float.parseFloat(scanner.next()); 

è di quasi 7 volte più veloce.

Per aggiungere ulteriori informazioni a questa risposta, l'origine dei problemi di prestazioni con il metodo è che utilizza un'espressione regolare per cercare il successivo float, che non è necessario se si conosce la struttura dei dati che si stanno leggendo in anticipo.

Si scopre la maggior parte (se non tutti) i next* utilizzare espressioni regolari per un motivo simile, quindi se si conosce la struttura dei dati è preferibile sempre uso next() e analizzare il risultato. OSSIA utilizzare anche Double.parseDouble(scanner.next()) e Integer.parseInt(scanner.next()).

fonte pertinente: https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/java/util/Scanner.java

+1

Va bene! Quello è buono. – limitfan

+0

Ho cercato di velocizzare il mio metodo di scansione e questo ha fatto il trucco. Wow ... +1 – semajhan

+1

Incredibile, solo questa semplice sostituzione ha aumentato la velocità di lettura dei float di un fattore 100 sul mio dispositivo di test (da 10 secondi a 0,1 secondi) – iseeall

2

Per la sfida Spotify hanno scritto una piccola utility Java per l'analisi IO più veloce: http://spc10.contest.scrool.se/doc/javaio L'utility si chiama Kattio.java e usa BufferedReader, StringTokenizer e Integer.parseInt/Double.parseDouble/Long. parseLunga da leggere i numeri.

1

Post molto perspicace. Normalmente quando lavoravo con Java pensavo che Scanner fosse più veloce su PC. Lo stesso quando provo ad usarlo su AsyncTask su Android, è WORST.

Penso che Android debba trovare un'alternativa allo scanner. Stavo usando scanner.nextFloat(); & scanner.nextDouble(); & scanner.nextInt(); tutti insieme che mi hanno fatto ammalare. Dopo aver tracciato la mia app, ho scoperto che il colpevole era nascosto.

ho cambiato per Float.parseFloat(scanner.next()); simile Double.parseDouble(scanner.next()); & Integer.parseInt(scanner.next());, che certamente ha reso la mia app abbastanza veloce Devo concordare, può essere il 60% più veloce.

Se qualcuno ha provato lo stesso, si prega di postare qui. E sto anche guardando un'alternativa all'API Scanner, chiunque ha idee brillanti può farsi avanti e postare qui sulla lettura dei formati di file.

+0

È da un po 'che non so quando ho pubblicato questo post, ma sembra che da allora non ci siano stati miglioramenti significativi. 'Float.parseFloat (scanner.next())' ti dà davvero un significativo aumento di velocità, ma non arriva nemmeno vicino alla lettura di numeri direttamente da un formato binario (circa il 1200% di aumento della velocità). Quindi, per grandi serie di numeri, consiglio comunque di convertire. A meno che la leggibilità del file non sia cruciale, in questo caso puoi lasciarlo come è per dev ma agganciare un'attività di generazione di risorse nel tuo sistema di build, per convertirlo per la produzione. –

+0

come posso leggerlo in un formato binario? Puoi dirmi un approccio adeguato? – zIronManBox

+0

Controlla 'DataInputStream' o converti i float in ints e usa 'InputStream' /' OutputStream' di base 'read' /' write'. È il modo più efficiente. –