2011-12-21 8 views
9

Sono molto nuovo in Java e sto cercando di utilizzare l'interfaccia Java di Mathematica per accedere a un file utilizzando la mappatura della memoria (nella speranza di un miglioramento delle prestazioni).Perché il metodo array() di MappedByteBuffer non funziona?

Il codice di Mathematica che ho è (credo) equivalente al seguente codice Java (sulla base di this):

import java.io.FileInputStream; 
import java.nio.MappedByteBuffer; 
import java.nio.channels.FileChannel; 

public class MainClass { 
    private static final int LENGTH = 8*100; 

    public static void main(String[] args) throws Exception { 
    MappedByteBuffer buffer = new FileInputStream("test.bin").getChannel().map(FileChannel.MapMode.READ_ONLY, 0, LENGTH); 
    buffer.load(); 
    buffer.isLoaded(); // returns false, why? 
    } 
} 

Vorrei utilizzare il metodo array() sul buffer, quindi sto cercando di caricare il buffer memorizza i contenuti in memoria usando prima load(). Tuttavia, anche dopo load(), isLoaded() restituisce false e buffer.array() genera un'eccezione: java.lang.UnsupportedOperationException at java.nio.ByteBuffer.array(ByteBuffer.java:940).

Perché il buffer non si carica e come posso chiamare il metodo array()?

Il mio obiettivo finale qui è quello di ottenere una serie di double s utilizzando asDoubleBuffer().array(). Il metodo getDouble() funziona correttamente, ma speravo di farlo in un colpo solo per ottenere buone prestazioni. Che cosa sto facendo di sbagliato?


come sto facendo questo da Mathematica, vi posterò il codice vero e proprio Mathematica ho usato troppo (equivalente a quanto sopra in Java):

Needs["JLink`"] 
LoadJavaClass["java.nio.channels.FileChannel$MapMode"] 
buffer = JavaNew["java.io.FileInputStream", "test.bin"]@getChannel[]@map[FileChannel$MapMode`READUONLY, 0, 8*100] 

[email protected][] 
[email protected][] (* returns False *) 
+0

"Un valore di ritorno falso non implica necessariamente che il contenuto del buffer non risieda nella memoria fisica." 'load' è solo il massimo degli sforzi, e in effetti potrebbe aver caricato i dati nella memoria fisica solo per essere immediatamente scambiati. –

+0

@ TomHawtin-tackline Penso di aver frainteso lo scopo di "caricare" allora. Quello che mi piacerebbe ottenere è quello di ottenere il contenuto del buffer come una serie di doppi. Il metodo 'array' non frazionato non funziona e getta l'eccezione che ho menzionato. Ho aggiornato la domanda in base al tuo feedback. – Szabolcs

+1

'array' funziona solo per i buffer che sono supportati da un array (in genere da' * Buffer.wrap'). –

risposta

4

Secondo Javadoc
"The il contenuto di un buffer di byte mappato può cambiare in qualsiasi momento, ad esempio se il contenuto della regione corrispondente del file mappato viene modificato da questo o da un altro programma, indipendentemente dal fatto che tali cambiamenti si verifichino e quando si verificano, dipende dal sistema operativo e quindi non specificato

Tutto o parte di un buffer di byte mappato può diventare inaccessibile in qualsiasi momento, ad esempio se il file mappato viene troncato. Un tentativo di accedere ad una regione inaccessibile di un buffer di byte mappato non cambierà il contenuto del buffer e farà sì che un'eccezione non specificata venga lanciata al momento dell'accesso o in un secondo momento. Si raccomanda pertanto vivamente di adottare precauzioni adeguate per evitare la manipolazione di un file mappato da questo programma o da un programma in esecuzione simultaneamente, tranne leggere o scrivere il contenuto del file. "

Per me sembra a molti . condizioni e cattivo comportamento indesiderato avete bisogno di particolare questa classe

Se avete solo bisogno di leggere il contenuto dei file in modo più veloce, dare una prova:?

FileChannel fChannel = new FileInputStream(f).getChannel(); 
    byte[] barray = new byte[(int) f.length()]; 
    ByteBuffer bb = ByteBuffer.wrap(barray); 
    bb.order(ByteOrder.LITTLE_ENDIAN); 
    fChannel.read(bb); 

funziona a velocità quasi uguale a velocità di prova del sistema su disco

Per il doppio è possibile utilizzare DoubleBuffer (con doppia [] array se f.length()/4 dimensioni) o semplicemente chiamare getDouble (int) metodo ByteBuffer.

+0

Non ho bisogno di questa classe in particolare. Speravo di essere in grado di ottenere il contenuto di una parte di un file binario molto grande (non tutto) come una serie di doppi, in un modo più veloce di quello che fornisce la funzionalità integrata di Mathematica. – Szabolcs

+0

Risposta aggiornata con "Per il doppio è possibile utilizzare DoubleBuffer (con array double [] di dimensioni f.length()/4) o semplicemente chiamare il metodo getDouble (int) di ByteBuffer." Hai detto che leggeresti solo alcuni doppi. Userò ByteBuffer per evitare la possibile conversione di byte non necessari in duplicati (nel caso di DoubleBuffer). – andrey

+0

Grazie a @andrey. Ho bisogno di ottenere un 'double []' alla fine, poiché questo è ciò che viene convertito automaticamente in un oggetto Mathematica (ad esempio, non posso semplicemente usare 'getDouble' con un indice). Se provo a leggere direttamente in 'DoubleBuffer', non funziona. Se leggo in un 'ByteBuffer', funziona, e posso ottenerlo' asDoubleBuffer() ', ma ciò che ottengo in questo modo restituisce' false' per 'hasArray()' di nuovo, cioè non riesco a convertirlo in una schiera di 'double's. Hai qualche suggerimento su come ottenere una matrice di double senza fare il loop in modo esplicito e copiarli uno per uno? – Szabolcs

0

in Java:

final byte[] hb;     // Non-null only for heap buffers 

quindi non è nemmeno implementato per MappedByteBuffer ma è per HeapByteBuffer.

in Android:

** 
    * Child class implements this method to realize {@code array()}. 
    * 
    * @see #array() 
    */ 
    abstract byte[] protectedArray(); 

e ancora non in MappedByteBuffer, ma per esempio ByteArrayBuffer realizza il supporto matrice.

@Override byte[] protectedArray() { 
    if (isReadOnly) { 
     throw new ReadOnlyBufferException(); 
    } 
    return backingArray; 
    } 

Il punto della mappa di memoria deve essere fuori heap. Un array di supporto sarebbe in heap.
Se è possibile aprire FileChannel da RandomAccessFile e quindi chiamare map sul canale, è possibile anche utilizzare il metodo bulk get() sul MappedByteBuffer per leggere in un byte []. Questo copia da heap, evitando l'IO, di nuovo nell'heap.

buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); 
byte[] b = new byte[buffer.limit()]; 
buffer.get(b);