2013-04-15 13 views
5

Używam Java 1.5 na wbudowanym urządzeniu linuksowym i chcę odczytać plik binarny z 2 MB wartości int. (Obecnie 4bytes Big Endian, ale mogę zdecydować, format)Najszybszy sposób na odczytanie ogromnej ilości int z pliku binarnego

Korzystanie DataInputStream poprzez BufferedInputStream wykorzystaniem dis.readInt()), te 500 000 połączeń potrzebuje 17s czytać, ale plik przeczytać w jeden duży bufor bajtowy potrzebuje 5 sekund.

Jak mogę szybciej odczytać ten plik w jednym wielkim int []?

Proces czytania nie powinien przekraczać 512 kb.

Ten kod poniżej przy użyciu nio nie jest szybszy niż metoda readInt() z 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(); 

Aktualizacja: Ocena odpowiedzi:

Na PC mapy pamięci z podejściem IntBuffer był najszybszy w moim skonfigurować.
na wbudowanym urządzeniu nie JIT java.io DataiInputStream.readInt() był nieco szybciej (17S, VS 20s do MemMap z IntBuffer)

Wnioski końcowe: Znaczne przyspieszenie jest łatwiej osiągnąć przez Zmiana algorytmu. (Mniejszy plik dla init)

+0

Proszę również sprawdzić http://makeprogrammingyourforte.blogspot.in/2012/09/fastest-way-to-read-input-in-java.html – Algorithmist

+0

@Algorithmist Sprawdziłem twój link, ale czyta z tekstu plik – AlexWien

+0

Berkeley ma dostępne rozszerzenie JK JK luzem [tutaj] (http://www.cs.berkeley.edu/~bonachea/java/). Nie używałem tego, ale może to wyglądać dobrze. –

Odpowiedz

4

ja nie wiem, czy to będzie szybciej niż Alexander zapewnione, ale można spróbować mapowanie pliku.

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

dzięki, że spróbuję jutro i opublikuję wynik. – AlexWien

+1

Na PC było to najszybsze rozwiązanie, ale na osadzonym bez JIT zajęło 20 sekund, więc java io jest nadal najszybszy. Interesujące ... – AlexWien

3

Można użyć IntBuffer z pakietu nio ->http://docs.oracle.com/javase/6/docs/api/java/nio/IntBuffer.html

int[] intArray = new int[ 5000000 ]; 

IntBuffer intBuffer = IntBuffer.wrap(intArray); 

... 

Wypełnić w buforze, poprzez wykonywanie połączeń do inChannel.read(intBuffer).

Po zapełnieniu bufora Twój intArray będzie zawierał 500000 liczb całkowitych.

EDIT

po sobie sprawę, że kanały obsługują jedynie 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

Próbowałem już tego, ale utknąłem w "int bytesRead = inChannel.read (intBuffer);" To się nie kompiluje, nie mogę przekazać IntBuffer do inChannel.read(), expoects byteBuffer – AlexWien

+0

@AlexWien. Zobacz zmiany do mojego posta –

+0

Wielkie dzięki, teraz działa, ale wykorzystuje 25 sekund na moim urządzeniu – AlexWien

2

wpadłem dość starannego eksperyment z wykorzystaniem serializacji/deserializacji, DataInputStream vs ObjectInputStream, zarówno na podstawie ByteArrayInputStream uniknięcia skutków IO. Dla miliona int, readObject miał około 20 ms, readInt miał około 116. Szeregowe obciążenie szeregujące w tablicy o liczbie milionów int wynosiło 27 bajtów. To było na MacBook Pro z 2013 roku.

Powiedziawszy to, serializacja obiektów jest czymś złym, i musisz zapisać dane za pomocą programu Java.

+0

To jest interesujące, nie rozważałem możliwości użycia writeObject. writeObject wewnętrznie wypełnia bajt [] używając Bits.putInt() przed wypisaniem. Może to być szybsze niż zwykłe wywoływanie writeInt() miliona razy. (Plik java.nio jest szybszy na komputerze niż java.io, ponieważ korzysta z dostępu DMA do dysku, który nie jest dostępny na tym urządzeniu wbudowanym) – AlexWien

Powiązane problemy