2010-10-27 22 views
5

Estoy trabajando en una aplicación de Android (en Java, obviamente) y recientemente actualicé mi código de lector UDP. En ambas versiones, he creado algunas memorias intermedias y recibir un paquete UDP:¿Las clases de ByteBuffer/IntBuffer/ShortBuffer Java son rápidas?

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

En la versión inicial, puse los datos de nuevo juntos un byte a la vez (en realidad 16 PCM datos de audio):

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

En la versión actualizada, que utiliza algunas herramientas Java fresco que no conocía de cuando empecé:

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

En ambos casos, "contar" se está rellena correctamente (he comprobado). Sin embargo, parece haber nuevos problemas con mi transmisión de audio, tal vez no se esté manejando lo suficientemente rápido, lo cual no tiene ningún sentido para mí. Obviamente, el código del búfer se está compilando en más de tres declaraciones de código JVM, pero desde el comienzo parecía una suposición razonable que la 2da versión sería más rápida que la 1ra.

Patentemente, no estoy insistiendo en que mi código TIENE que usar búferes Java NIO, pero a primera vista, al menos, parece una mo 'betta' hacer esto.

¿Alguien tiene alguna recomendación para un lector de UDP Java simple y rápido y si existe una "mejor manera" generalmente aceptada?

Gracias, R.

+0

NIO no está destinado a ser más rápido que el IO "normal", es simplemente más escalable. – skaffman

+0

'robó la sintaxis' ¿Lol, hablas en serio? Google robó la sintaxis de Java tanto como un autor la sintaxis en inglés – Falmarri

+0

@Tim Bender, ¿qué tiene eso que ver con la pregunta? Puede haber diferencias entre el Dalvik vm y el jvm estándar, pero en general si las cosas son lentas en el jvm, es probable que también sean lentas en Android. ¿Tiene algún conocimiento de una diferencia que podría afectar este caso particular? –

Respuesta

1

En general, se trabaja con tipos primitivos directamente va a ser más eficiente que trabaja con objetos porque se evita algunos de los gastos generales de la creación de objetos, las llamadas de función, etc.

Existen razones para usar los objetos de utilidad distintos de la velocidad: comodidad, seguridad, etc.

La mejor manera de probar la diferencia en este caso particular sería medirla realmente. Pruebe ambos métodos con un gran conjunto de datos y cronometralo. Entonces, puede decidir si vale la pena los beneficios en este caso.

También puede usar el generador de perfiles de Android para ver dónde están realmente sus problemas. Ver TraceView.

+0

Parece que he desencadenado una guerra religiosa ... Lo siento, y gracias por la racionalidad. – Rich

+0

Algunas de las cosas de NIO son bastante horribles en este momento. Se han realizado mejoras y comenzarán a aparecer en versiones futuras. Mientras tanto, "probar y medir" es una buena respuesta. – fadden

0

Usaría DataInputStream para esta tarea, envuelto alrededor de un ByteArrayInputStream alrededor de la matriz de bytes. Encapsula el desplazamiento en readShort() sin los gastos generales de ByteBuffer.

Alternativamente, podría leer directamente en un DirectByteBuffer a través de un DatagramChannel, en lugar de utilizar el DatagramPacket & DatagramSocket. Actualmente estás usando una mezcla de java.net y java.nio.

No esperaría grandes diferencias de rendimiento entre estos dos enfoques, pero espero que ambos sean más rápidos que su enfoque híbrido.

+0

* Encapsula los cambios en readShort() sin los gastos generales de ByteBuffer. * ReadShort tiene más sobrecarga que la misma operación de un ByteBuffer – bestsss

+0

@bestsss: 'overheads' plural: especialmente los gastos generales de creación. Es a esos a los que me refiero. – EJP

+0

ByteBuffer directo tiene cierta asignación (y desasignación, debido al finalizador basado en uno) pero ByteArrayInputStream tiene el mismo costo de asignación que ByteBuffer.wrap y como un bono todos los métodos están sincronizados, además ByteBuffer tiende a disfrutar de mayor intrínseco por el JIT. Además, no crea un ByteBuffer directo para cada paquete, sino una vez por socket (o lo saca de un grupo). – bestsss

3

Su código sería más eficiente si en lugar de leer un paquete en una matriz de bytes (copiando los datos de un búfer nativo en la matriz) y luego envolverlo en un nuevo ByteBuffer (creando un nuevo objeto) y convertirlo en un ShortBuffer (crear un nuevo objeto) configura sus objetos solo una vez y evita la copia.

Puede hacer esto utilizando DatagramChannel.socket() para crear su socket, luego conectarlo como de costumbre y usar socket.getChannel() para obtener un objeto DatagramChannel.Este objeto le permitirá leer paquetes directamente en un ByteBuffer existente (que debe crear con ByteBuffer.allocateDirect para obtener la máxima eficacia). A continuación, puede usarnos como ShortBuffer() una sola vez para crear una vista de sus datos como cortos y leer de ese ShortBuffer cada vez que rellene el ByteBuffer.

El código, por tanto, tiene el siguiente aspecto:

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()); 

Usted debe encontrar esto es mucho más eficiente que el código anterior, ya que evita toda una copia de los datos y la conversión del formato es manejado por código optimizado a mano en lugar de compilación generada por manipulación de bytes que puede ser subóptima. Será algo más eficiente si usa el orden de bytes nativo de la plataforma (creo que Android usa el orden de bytes little-endian en todas las plataformas para las que está disponible, y su código anterior parece ser big-endian, por lo que es posible que esto no sea posible para ti), en cuyo caso shortBuf.get() se convierte en una copia de memoria directa.

+0

Buenas respuestas Jules, no estoy seguro de por qué este no es el aceptado. –

Cuestiones relacionadas