sobre servidor recibir enviar ejemplo datagramsocket datagrampacket cliente java android performance sockets casting

java - servidor - sockets udp



Enviar eficientemente grandes int[] sobre sockets en Java (4)

Como noté en un comentario, creo que estás golpeando los límites de tu procesador. Como esto podría ser útil para otros, lo dividiré. Aquí está su bucle para convertir números enteros en bytes:

for(int i = 0; i < input.length; i++) { output[i*4] = (byte)(input[i] & 0xFF); output[i*4 + 1] = (byte)((input[i] & 0xFF00) >>> 8); output[i*4 + 2] = (byte)((input[i] & 0xFF0000) >>> 16); output[i*4 + 3] = (byte)((input[i] & 0xFF000000) >>> 24); }

Este ciclo se ejecuta 500,000 veces. Su procesador de 600Mhz puede procesar aproximadamente 600,000,000 de operaciones por segundo. Por lo tanto, cada iteración del ciclo consumirá aproximadamente 1/1200 de segundo por cada operación.

De nuevo, usando números muy aproximados (no sé el conjunto de instrucciones ARM, por lo que puede haber más o menos por acción), aquí hay un conteo de operaciones:

  • Prueba / bifurcación: 5 (recupera el contador, recupera la longitud de la matriz, compara, ramifica, incrementa el contador)
  • Máscara y desplazamiento: 10 x 4 (recupera el contador, recupera la base del conjunto de entrada, agrega, recupera la máscara y, desplaza, multiplica el contador, agrega el desplazamiento, agrega a la base de salida, almacena)

OK, así que en números aproximados, este ciclo toma como mucho 55/1200 de segundo, o 0.04 segundos. Sin embargo, no estás lidiando con el mejor de los casos. Por un lado, con una matriz tan grande, no va a beneficiarse de un caché de procesador, por lo que introducirá los estados de espera en cada almacén de array y se cargará.

Además, las operaciones básicas que describí pueden traducirse o no directamente en código de máquina. Si no (y sospecho que no), el ciclo costará más de lo que he descrito.

Finalmente, si no tiene mucha suerte, la JVM no ha JIT-ed su código, por lo que para una parte (o la totalidad) del bucle está interpretando bytecode en lugar de ejecutar instrucciones nativas. No sé lo suficiente sobre Dalvik para comentar sobre eso.

Estoy trabajando en una aplicación Java donde necesito enviar una matriz de 500,000 enteros desde un teléfono Android a otro teléfono Android a través de una conexión de socket lo más rápido posible. El cuello de botella principal parece estar convirtiendo los enteros para que el socket pueda tomarlos, ya sea que use ObjectOutputStreams, ByteBuffers o una conversión de máscara y cambio de bajo nivel. ¿Cuál es la forma más rápida de enviar un int [] sobre un socket de una aplicación Java a otra?

Aquí está el código de todo lo que he intentado hasta ahora, con puntos de referencia en el LG Optimus V que estoy probando (procesador ARM de 600 MHz, Android 2.2).

Nivel bajo de máscara y cambio: 0.2 segundos

public static byte[] intToByte(int[] input) { byte[] output = new byte[input.length*4]; for(int i = 0; i < input.length; i++) { output[i*4] = (byte)(input[i] & 0xFF); output[i*4 + 1] = (byte)((input[i] & 0xFF00) >>> 8); output[i*4 + 2] = (byte)((input[i] & 0xFF0000) >>> 16); output[i*4 + 3] = (byte)((input[i] & 0xFF000000) >>> 24); } return output; }

Usando ByteBuffer e IntBuffer: 0.75 segundos

public static byte[] intToByte(int[] input) { ByteBuffer byteBuffer = ByteBuffer.allocate(input.length * 4); IntBuffer intBuffer = byteBuffer.asIntBuffer(); intBuffer.put(input); byte[] array = byteBuffer.array(); return array; }

ObjectOutputStream: 3.1 segundos (Probé variaciones de esto usando DataOutPutStream y writeInt () en lugar de writeObject (), pero no marcó mucha diferencia)

public static void sendSerialDataTCP(String address, int[] array) throws IOException { Socket senderSocket = new Socket(address, 4446); OutputStream os = senderSocket.getOutputStream(); BufferedOutputStream bos = new BufferedOutputStream (os); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(array); oos.flush(); bos.flush(); os.flush(); oos.close(); os.close(); bos.close(); senderSocket.close(); }

Por último, el código que solía enviar byte []: toma una adición de 0.2 segundos sobre las funciones intToByte ()

public static void sendDataTCP(String address, byte[] data) throws IOException { Socket senderSocket = new Socket(address, 4446); OutputStream os = senderSocket.getOutputStream(); os.write(data, 0, data.length); os.flush(); senderSocket.close(); }

Estoy escribiendo el código en ambos lados del socket para poder probar cualquier tipo de endianness, compresión, serialización, etc. Tiene que haber una forma de hacer esta conversión de manera más eficiente en Java. ¡Por favor ayuda!


Java IMO nunca tuvo la intención de poder reinterpretar de manera eficiente una región de memoria de int[] a byte[] como lo haría en C. Ni siquiera tiene ese modelo de dirección de memoria.

O bien debe ser nativo para enviar los datos o puede intentar encontrar algunas micro optimizaciones. Pero dudo que ganes mucho.

Por ejemplo, esto podría ser un poco más rápido que tu versión (si funciona)

public static byte[] intToByte(int[] input) { byte[] output = new byte[input.length*4]; for(int i = 0; i < input.length; i++) { int position = i << 2; output[position | 0] = (byte)((input[i] >> 0) & 0xFF); output[position | 1] = (byte)((input[i] >> 8) & 0xFF); output[position | 2] = (byte)((input[i] >> 16) & 0xFF); output[position | 3] = (byte)((input[i] >> 24) & 0xFF); } return output; }


Lo haría así:

Socket senderSocket = new Socket(address, 4446); OutputStream os = senderSocket.getOutputStream(); BufferedOutputStream bos = new BufferedOutputStream(os); DataOutputStream dos = new DataOutputStream(bos); dos.writeInt(array.length); for(int i : array) dos.writeInt(i); dos.close();

Por otro lado, léalo como:

Socket recieverSocket = ...; InputStream is = recieverSocket.getInputStream(); BufferedInputStream bis = new BufferedInputStream(is); DataInputStream dis = new DataInputStream(bis); int length = dis.readInt(); int[] array = new int[length]; for(int i = 0; i < length; i++) array[i] = dis.readInt(); dis.close();


Si no está en desacuerdo con el uso de una biblioteca, es posible que desee verificar los Buffers de Protocolo de Google. Está diseñado para una serialización de objetos mucho más compleja, pero apostaría a que trabajaron duro para descubrir cómo serializar rápidamente una matriz de enteros en Java.

EDITAR: Miré en el código fuente de Protobuf, y usa algo similar a su máscara de bajo nivel y cambio.