2010-10-27 7 views
5

Sto lavorando su un'applicazione Android (ovviamente in Java) e di recente ho aggiornato il mio codice lettore UDP. In entrambe le versioni, ho creato alcuni buffer e ricevere un pacchetto UDP:Le classi Java ByteBuffer/IntBuffer/ShortBuffer sono veloci?

byte[] buf = new byte[10000]; 
short[] soundData = new short[1000]; 
DatagramPacket packet = new DatagramPacket (buf, buf.length); 
socket.receive (packet); 

Nella versione iniziale, ho messo i dati di nuovo insieme un byte alla volta (in realtà è 16 PCM dati audio):

for (int i = 0; i < count; i++) 
    soundData[i] = (short) (((buf[k++]&0xff) << 8) + (buf[k++]&0xff)); 

Nella versione aggiornata, ho usato alcuni strumenti Java interessanti che non sapevo quando ho iniziato:

bBuffer = ByteBuffer.wrap (buf); 
sBuffer = bBuffer.asShortBuffer(); 
sBuffer.get (soundData, 0, count); 

In entrambi i casi, "contare" viene popolata correttamente (ho controllato). Tuttavia, sembra che ci siano nuovi problemi con il mio streaming audio - forse non viene gestito abbastanza velocemente - che non ha alcun senso per me. Ovviamente, il codice del buffer si sta compilando in molte più di tre dichiarazioni di codice JVM, ma è sembrato sicuramente un presupposto ragionevole quando avvio che la seconda versione sarebbe più veloce della prima.

Patentemente, non sto insistendo sul fatto che il mio codice DEVE utilizzare i buffer NIO Java, ma a prima vista, almeno, sembra proprio un 'betta' fare questo.

Qualcuno ha qualche raccomandazione per un lettore Java UDP veloce e semplice e se esiste un "modo migliore" generalmente accettato?

Grazie, R.

+0

NIO non è progettato per essere più veloce di IO "normale", è solo più scalabile. – skaffman

+0

'rubato la sintassi' Lol sei serio? Google ha rubato la sintassi java tanto quanto un autore elabora la sintassi inglese – Falmarri

+0

@Tim Bender che cosa ha a che fare con la domanda? Potrebbero esserci delle differenze tra il vm di Dalvik rispetto al jvm standard, ma in generale se le cose sono lente nella jvm è probabile che siano anche lente su Android. Hai una certa conoscenza di una differenza che potrebbe influenzare questo caso particolare? –

risposta

1

In generale, lavorando con tipi primitivi direttamente sta per essere più efficiente di lavorare con oggetti poiché si evita l'overhead di creare oggetti, chiamate di funzione, ecc

Ci sono motivi per utilizzare gli oggetti di utilità diversi dalla velocità: convenienza, sicurezza, ecc.

Il modo migliore per verificare la differenza in questo caso particolare sarebbe misurarlo effettivamente. Prova entrambi i metodi con un grande set di dati e cronometralo. Quindi, puoi decidere se ne vale la pena in questo caso.

Puoi anche utilizzare il profiler di Android per vedere dove sono realmente i tuoi problemi. Vedi TraceView.

+0

Mi sembra di aver toccato una guerra religiosa ... Scusa, e grazie per la razionalità. – Rich

+0

Alcune delle cose NIO sono piuttosto orribili al momento. Sono stati apportati miglioramenti e inizieranno a comparire nelle versioni future. Nel frattempo, "test e misura" è una buona risposta. – fadden

0

Vorrei utilizzare DataInputStream per questa attività, avvolto attorno a un ByteArrayInputStream avvolto attorno alla matrice di byte. Incapsula lo spostamento in readShort() senza i costi generali di ByteBuffer.

In alternativa, è possibile leggere direttamente in un DirectByteBuffer tramite un DatagramChannel, anziché utilizzare DatagramPacket & DatagramSocket. Al momento stai usando una combinazione di java.net e java.nio.

Non mi aspetto grandi differenze nelle prestazioni tra questi due approcci, ma mi aspetto che siano entrambi più veloci del tuo approccio ibrido.

+0

* Incapsula lo spostamento in readShort() senza i sovraccarichi di ByteBuffer. * ReadShort ha un sovraccarico maggiore rispetto alla stessa operazione di un ByteBuffer – bestsss

+0

@bestsss: "overheads" plurale: in particolare i costi generali di creazione. Sono quelli a cui mi riferisco. – EJP

+0

diretto ByteBuffer ha qualche allocazione (e deallocazione, a causa di uno basato sul finalizzatore) ma ByteArrayInputStream ha lo stesso costo di allocazione di ByteBuffer.wrap e come bonus tutti i metodi sono sincronizzati, più ByteBuffer tende a godere di un'intrinseca intrinseca JIT. Inoltre non si crea un ByteBuffer diretto per ogni pacchetto ma una volta per socket (o lo si toglie da un pool). – bestsss

3

Il codice sarebbe più efficace se invece di lettura di un pacchetto in un array di byte (copiando i dati da un buffer nativo nella matrice) e poi avvolgendolo in una nuova ByteBuffer (creando un nuovo oggetto) e la conversione di un ShortBuffer (creazione di un nuovo oggetto) si imposta gli oggetti una sola volta e si evita la copia.

È possibile farlo utilizzando DatagramChannel.socket() per creare il socket, quindi collegarlo come al solito e utilizzando socket.getChannel() per ottenere un oggetto DatagramChannel.Questo oggetto ti permetterà di leggere i pacchetti direttamente in un ByteBuffer esistente (che dovresti creare con ByteBuffer.allocateDirect per la massima efficienza). Puoi quindi utilizzare asShortBuffer() una sola volta per creare una visualizzazione dei tuoi dati come cortometraggi e leggere da ShortBuffer dopo ogni ricarica di ByteBuffer.

Il codice sembra quindi in questo modo:

DatagramSocket socket = DatagramChannel.socket(); 
// code to connect socket 
DatagramChannel channel = socket.getChannel(); 
ByteBuffer buffer = ByteBuffer.allocateDirect (10000); 
// you may want to invoke buffer.order(...) here to tell it what byte order to use 
ShortBuffer shortBuf = buffer.asShortBuffer(); 

// in your receive loop: 
buffer.clear(); 
channel.receive(buffer); 
shortBuf.position(0).limit(buffer.position()/2); // may ignore a byte if odd number received 
shortBuf.get(soundBuf,0,shortBuf.limit()); 

Si dovrebbe trovare questo è molto più efficiente rispetto il codice precedente perché evita tutta una copia dei dati e la conversione del formato viene gestita dal codice ottimizzato a mano piuttosto che la manipolazione di byte generata dal compilatore che può essere subottimale. Sarà un po 'più efficiente se si utilizza l'ordine di byte nativo della piattaforma (credo che Android usi l'ordine di byte little-endian su tutte le piattaforme per cui è disponibile, e il tuo codice sopra sembra essere big-endian, quindi questo potrebbe non essere possibile per te), nel qual caso shortBuf.get() diventa una copia di memoria diretta.

+0

Grandi risposte Jules, non so perché questo non è accettato. –