logo java performance nio bytebuffer

java - logo - hbase



ByteBuffer.allocate() vs. ByteBuffer.allocateDirect() (4)

ya que DirectByteBuffers es una asignación de memoria directa en el nivel del sistema operativo

Ellos no son Son simplemente memoria de proceso de aplicación normal, pero no están sujetos a reubicación durante Java GC, lo que simplifica considerablemente las cosas dentro de la capa JNI. Lo que describes se aplica a MappedByteBuffer .

que funcionaría más rápido con llamadas get / put

La conclusión no sigue de la premisa; la premisa es falsa; y la conclusión es también falsa Son más rápidos una vez que ingresas a la capa JNI, y si estás leyendo y escribiendo desde el mismo DirectByteBuffer , son mucho más rápidos, porque los datos nunca tienen que cruzar el límite JNI.

Para allocate() o para allocateDirect() , esa es la pregunta.

Desde hace algunos años, me limito a pensar que, dado que los DirectByteBuffer son una asignación de memoria directa a nivel de sistema operativo, funcionarían más rápido con las llamadas de obtener / poner que las de HeapByteBuffer . Nunca estuve realmente interesado en conocer los detalles exactos sobre la situación hasta ahora. Quiero saber cuáles de los dos tipos de ByteBuffer son más rápidos y en qué condiciones.


Lo mejor es hacer tus propias mediciones. La respuesta rápida parece ser que el envío desde un búfer allocateDirect() tarda entre un 25% y un 75% menos de tiempo que la variante allocate() (probado como copiar un archivo en / dev / null), dependiendo del tamaño, pero que la asignación misma puede ser significativamente más lento (incluso por un factor de 100x).

Fuentes:


No hay ninguna razón para esperar que los búferes directos sean más rápidos para acceder dentro de jvm. Su ventaja viene cuando los pasas a código nativo, como el código detrás de canales de todo tipo.


Ron Hitches en su excelente libro Java NIO parece ofrecer lo que pensé podría ser una buena respuesta a su pregunta:

Los sistemas operativos realizan operaciones de E / S en áreas de memoria. Estas áreas de memoria, en lo que respecta al sistema operativo, son secuencias contiguas de bytes. Entonces no es sorprendente que solo los búferes de byte sean elegibles para participar en operaciones de E / S. Recuerde también que el sistema operativo accederá directamente al espacio de direcciones del proceso, en este caso, el proceso de JVM, para transferir los datos. Esto significa que las áreas de memoria que son objetivos de las peraciones de E / S deben ser secuencias contiguas de bytes. En la JVM, una matriz de bytes no se puede almacenar contiguamente en la memoria, o el recolector de basura podría moverla en cualquier momento. Las matrices son objetos en Java y la forma en que se almacenan los datos dentro de ese objeto puede variar de una implementación de JVM a otra.

Por esta razón, se introdujo la noción de un buffer directo. Los búferes directos están destinados a la interacción con canales y rutinas de E / S nativas. Hacen un mejor esfuerzo para almacenar los elementos de bytes en un área de memoria que un canal puede usar para el acceso directo o en bruto mediante el uso de código nativo para indicar al sistema operativo que drene o llene el área de la memoria directamente.

Los búferes de byte directo suelen ser la mejor opción para las operaciones de E / S. Por diseño, admiten el mecanismo de E / S más eficiente disponible para la JVM. Los búferes de byte no directo se pueden pasar a canales, pero hacerlo puede incurrir en una penalización de rendimiento. Por lo general, no es posible que un almacenamiento intermedio no directo sea el objetivo de una operación de E / S nativa. Si transfiere un objeto ByteBuffer no directo a un canal para escritura, el canal puede hacer lo siguiente implícitamente en cada llamada:

  1. Crea un objeto ByteBuffer directo temporal.
  2. Copie el contenido del búfer no directo en el búfer temporal.
  3. Realice la operación de E / S de bajo nivel utilizando el búfer temporal.
  4. El objeto de búfer temporal queda fuera del alcance y finalmente se recolecta la basura.

Esto puede dar como resultado la copia de buffer y la rotación de objetos en cada E / S, que son exactamente el tipo de cosas que nos gustaría evitar. Sin embargo, dependiendo de la implementación, las cosas pueden no ser tan malas. El tiempo de ejecución probablemente almacenará en caché y reutilizará búferes directos o realizará otros trucos inteligentes para aumentar el rendimiento. Si simplemente está creando un búfer para un solo uso, la diferencia no es significativa. Por otro lado, si va a utilizar el búfer varias veces en un escenario de alto rendimiento, es mejor asignar buffers directos y reutilizarlos.

Los búferes directos son óptimos para E / S, pero pueden ser más costosos de crear que los búferes de byte no directo. La memoria utilizada por los búferes directos se asigna llamando al código nativo específico del sistema operativo, pasando por alto el montón de JVM estándar. Configurar y destruir los búferes directos podría ser significativamente más costoso que los búferes residentes en el montón, según el sistema operativo del host y la implementación de JVM. Las áreas de almacenamiento de memoria de los búferes directos no están sujetas a la recolección de basura porque están fuera del montón de JVM estándar.

Las compensaciones de rendimiento del uso de almacenamientos intermedios directos versus no directos pueden variar ampliamente mediante JVM, sistema operativo y diseño de código. Al asignar memoria fuera del montón, puede someter su aplicación a fuerzas adicionales de las cuales la JVM no tiene conocimiento. Al poner en juego partes móviles adicionales, asegúrese de estar logrando el efecto deseado. Recomiendo la antigua máxima del software: primero haz que funcione, luego hazlo rápido. No se preocupe demasiado por la optimización desde el principio; concéntrate primero en la corrección. La implementación de JVM puede realizar caché de búfer u otras optimizaciones que le proporcionarán el rendimiento que necesita sin mucho esfuerzo innecesario por su parte.