2013-04-15 4 views
5

Io uso Java 1.5 su un dispositivo Linux incorporato e voglio leggere un file binario con 2 MB di valori int. (Ora 4bytes Big Endian, ma posso decidere, il formato)Il modo più veloce per leggere un numero enorme di int dal file binario

Utilizzando DataInputStream via BufferedInputStream utilizzando dis.readInt()), questi 500 000 chiamate bisogno 17S da leggere, ma il file letto in un buffer grande byte ha bisogno di 5 secondi.

Come posso leggere quel file più velocemente in un unico enorme []?

Il processo di lettura non deve utilizzare più di 512 kb in aggiunta.

Questo codice di seguito che utilizza nio non è più veloce dell'approccio readInt() da java io.

// asume I already know that there are now 500 000 int to read: 
    int numInts = 500000; 
    // here I want the result into 
    int[] result = new int[numInts]; 
    int cnt = 0; 

    RandomAccessFile aFile = new RandomAccessFile("filename", "r"); 
    FileChannel inChannel = aFile.getChannel(); 

    ByteBuffer buf = ByteBuffer.allocate(512 * 1024); 

    int bytesRead = inChannel.read(buf); //read into buffer. 

    while (bytesRead != -1) { 

     buf.flip(); //make buffer ready for get() 

     while(buf.hasRemaining() && cnt < numInts){ 
     // probably slow here since called 500 000 times 
      result[cnt] = buf.getInt(); 
      cnt++; 
     } 

     buf.clear(); //make buffer ready for writing 
     bytesRead = inChannel.read(buf); 
    } 


    aFile.close(); 
    inChannel.close(); 

Aggiornamento: La valutazione delle risposte:

Su PC la mappa di memoria con l'approccio IntBuffer stato il più veloce nel mio set up.
Sul dispositivo embedded, senza JIT, il java.io DataiInputStream.readInt() è stato un po 'più veloce (17s, 20s vs per la memmap con IntBuffer)

Conclusione finale: velocità significativa up è più facile da raggiungere via Cambiamento algoritmico (File in formato più piccolo per init)

+0

Si prega di controllare anche http://makeprogrammingyourforte.blogspot.in/2012/09/fastest-way-to-read-input-in-java.html – Algorithmist

+0

@Algorithmist Ho controllato il tuo link, ma si legge da un testo presentare – AlexWien

+0

Berkeley ha una massa di estensione IO JNI disponibile [qui] (http://www.cs.berkeley.edu/~bonachea/java/). Non l'ho usato, ma potrebbe essere utile dare un'occhiata. –

risposta

4

Non so se questo sarà più veloce di quello che Alexander previsto, ma si potrebbe provare a mappare il file.

try (FileInputStream stream = new FileInputStream(filename)) { 
     FileChannel inChannel = stream.getChannel(); 

     ByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size()); 
     int[] result = new int[500000]; 

     buffer.order(ByteOrder.BIG_ENDIAN); 
     IntBuffer intBuffer = buffer.asIntBuffer(); 
     intBuffer.get(result); 
    } 
+0

grazie, proverò domani e pubblicherò il risultato. – AlexWien

+1

Su PC è stata la soluzione più veloce, ma su embedded senza JIT ci sono voluti 20 secondi, quindi java io è ancora più veloce. Interessante ... – AlexWien

3

È possibile utilizzare IntBuffer dalla confezione NIO ->http://docs.oracle.com/javase/6/docs/api/java/nio/IntBuffer.html

int[] intArray = new int[ 5000000 ]; 

IntBuffer intBuffer = IntBuffer.wrap(intArray); 

... 

Compilare il tampone, effettuando chiamate a inChannel.read(intBuffer).

Una volta che il buffer è pieno, il vostro intArray conterrà 500000 numeri interi.

EDIT

Dopo aver capito che supportano solo canali ByteBuffer.

// asume I already know that there are now 500 000 int to read: 
int numInts = 500000; 
// here I want the result into 
int[] result = new int[numInts]; 

// 4 bytes per int, direct buffer 
ByteBuffer buf = ByteBuffer.allocateDirect(numInts * 4); 

// BIG_ENDIAN byte order 
buf.order(ByteOrder.BIG_ENDIAN); 

// Fill in the buffer 
while (buf.hasRemaining()) 
{ 
    // Per EJP's suggestion check EOF condition 
    if(inChannel.read(buf) == -1) 
    { 
     // Hit EOF 
     throw new EOFException(); 
    } 
} 

buf.flip(); 

// Create IntBuffer view 
IntBuffer intBuffer = buf.asIntBuffer(); 

// result will now contain all ints read from file 
intBuffer.get(result); 
+0

L'ho già provato ma sono bloccato su "int bytesRead = inChannel.read (intBuffer);" Questo non si compila, non posso passare un IntBuffer a inChannel.read(), esso espone un byteBuffer – AlexWien

+0

@AlexWien. Vedi le modifiche al mio post –

+0

Molte grazie, ora funziona, ma utilizza 25 secondi sul mio dispositivo – AlexWien

2

ho fatto un esperimento abbastanza attenzione a utilizzare serializzare/deserializzare, DataInputStream vs ObjectInputStream, entrambi basati su ByteArrayInputStream per evitare gli effetti IO. Per un milione interi, readObject era di circa 20 msec, readInt era circa 116. L'overhead serializzazione su un array milione-int è 27 byte. Questo era su un MacBook Pro del 2013.

Detto questo, serializzazione degli oggetti è una sorta di male, e si deve aver scritto i dati con un programma Java.

+0

Questo è interessante, non ho considerato la possibilità di usare writeObject. writeObject internamente riempie un byte [] usando Bits.putInt() prima di scrivere. Questo potrebbe essere più veloce del semplice chiamare writeInt() un milione di volte. (Java.nio è più veloce sul PC rispetto a java.io, perché utilizza l'accesso DMA al disco, che non è disponibile su quel dispositivo incorporato) – AlexWien