Usar archivos para para la memoria compartida IPC, ¿es el mapeo de la memoria un requisito?

votos
19

Hay algunos proyectos que usan los MappedByteBuffers devueltos por el FileChannel.map() de Java como una forma de tener memoria compartida IPC entre JVMs en el mismo host (ver Chronicle Queue, Aeron IPC, etc.). Por lo que puedo decir, este IPC sólo se sienta encima de la llamada mmap. Sin embargo, la implementación de Java no permite mapeos anónimos (no respaldados por archivos).

Mi pregunta es, en Java (1.8) y Linux (3.10), ¿son realmente necesarios los MappedByteBuffers para implementar el IPC de memoria compartida, o cualquier acceso a un archivo común proporcionaría la misma funcionalidad? (Esta pregunta no tiene que ver con la implicación de rendimiento de usar un MappedByteBuffer o no)

Aquí está mi entendimiento:

  1. Cuando Linux carga un archivo desde el disco, copia el contenido de ese archivo a las páginas de la memoria. Esa región de la memoria se llama caché de páginas. Hasta donde puedo decir, lo hace sin importar qué método de Java (FileInputStream.read(), RandomAccessFile.read(), FileChannel.read(), FileChannel.map()) o método nativo se usa para leer el archivo (obsevado con free y monitoreando el valor de cache).
  2. Si otro proceso intenta cargar el mismo archivo (mientras aún está en la caché) el kernel lo detecta y no necesita recargar el archivo. Si el caché de páginas se llena, las páginas serán desalojadas - las sucias se escriben de nuevo en el disco. (Las páginas también se vuelven a escribir si hay una descarga explícita al disco, y periódicamente, con un hilo del núcleo).
  3. Tener un archivo (grande) ya en la caché es un aumento significativo del rendimiento, mucho más que las diferencias basadas en los métodos de Java que usamos para abrir/leer ese archivo.
  4. Un programa C que llama al sistema mmap puede hacer un mapeo ANÓNIMO, que esencialmente asigna páginas en la caché que no están respaldadas por un archivo real (por lo que no hay necesidad de emitir escrituras reales en el disco), pero Java no parece ofrecer eso (¿el mapeo de un archivo en tmpfs lograría lo mismo?)
  5. Si se carga un archivo usando la llamada de sistema mmap (C) o a través de FileChannel.map() (Java), esencialmente las páginas del archivo (en la caché) se cargan directamente en el espacio de direcciones del proceso. Utilizando otros métodos para abrir un archivo, éste se carga en páginas que no están en el espacio de direcciones del proceso, y luego los diversos métodos para leer/escribir ese archivo copian algunos bytes desde/hacia esas páginas en una memoria intermedia en el espacio de direcciones del proceso. Hay un obvio beneficio de rendimiento al evitar esa copia, pero mi pregunta no tiene que ver con el rendimiento.

Así que en resumen, si entiendo correctamente - mientras que el mapeo ofrece una ventaja de rendimiento, no parece que ofrezca ninguna funcionalidad de memoria compartida que no obtengamos ya sólo de la naturaleza del Linux y el caché de páginas.

Así que, por favor, hazme saber dónde está mi comprensión.

Gracias.

Publicado el 22/05/2020 a las 21:20
fuente por usuario
En otros idiomas...                            


2 respuestas

votos
0

Vale la pena mencionar tres puntos: rendimiento, y cambios concurrentes, y utilización de la memoria.

Usted está en lo cierto en la evaluación de que basado en MMAP usualmente ofrecerá una ventaja de rendimiento sobre el IO basado en archivos. En particular, la ventaja de rendimiento es significativa si el código realiza muchas IO pequeñas en cualquier punto del archivo.

considere la posibilidad de cambiar el N-ésimo byte: con mmap buffer[N] = buffer[N] + 1, y con el acceso basado en archivos necesita (al menos) 4 llamadas de sistema de comprobación de errores:

   seek() + error check
   read() + error check
   update value
   seek() + error check
   write + error check

Es cierto que el número de IO reales (al disco) probablemente sea el mismo.

El segundo punto que vale la pena señalar es el acceso simultáneo. Con IO basado en archivos, tienes que preocuparte por el potencial acceso concurrente. Tendrá que emitir un bloqueo explícito (antes de la lectura), y un desbloqueo (después de la escritura), para evitar que dos procesos accedan incorrectamente al valor al mismo tiempo. Con la memoria compartida, las operaciones atómicas pueden eliminar la necesidad de un bloqueo adicional.

El tercer punto es el uso real de la memoria. En los casos en que el tamaño de los objetos compartidos es significativo, el uso de la memoria compartida puede permitir que un gran número de procesos accedan a los datos sin asignar memoria adicional. Si los sistemas restringidos por la memoria, o los sistemas que necesitan proporcionar un rendimiento en tiempo real, ésta podría ser la única forma de acceder a los datos.

Respondida el 29/05/2020 a las 10:35
fuente por usuario

votos
0

Mi pregunta es, en Java (1.8) y Linux (3.10), ¿son realmente necesarios los MappedByteBuffers para implementar la memoria compartida de la IPC, o cualquier acceso a un archivo común proporcionaría la misma funcionalidad?

Depende de por qué quieres implementar el IPC de memoria compartida.

Claramente puedes implementar el IPC sin memoria compartida; por ejemplo, sobre los enchufes. Por lo tanto, si no lo hace por razones de rendimiento, no es necesario hacer el IPC con memoria compartida en absoluto!

Así que la actuación tiene que estar en la raíz de cualquier discusión.

El acceso mediante archivos a través de las APIs de Java classic io o nio no proporciona una funcionalidad o rendimiento de memoria compartida.

La principal diferencia entre la E/S de archivos regulares o E/S de sockets frente a la memoria compartida IPC es que la primera requiere que las aplicaciones hagan ready writellamen al sistema explícitamente para enviar y recibir mensajes. Esto implica llamadas extra al sistema, e implica que el núcleo copie los datos. Además, si hay múltiples hilos, se necesita un "canal" separado entre cada par de hilos o algo para multiplexar múltiples "conversaciones" sobre un canal compartido. Esto último puede llevar a que el canal compartido se convierta en un cuello de botella de la concurrencia.

Tengan en cuenta que estos gastos generales son ortogonales a la caché de páginas de Linux.

Por el contrario, con el IPC implementado usando memoria compartida, no readhay writellamadas al sistema, y no hay un paso de copia extra. Cada "canal" puede simplemente usar un área separada del buffer mapeado. Un hilo en un proceso escribe datos en la memoria compartida y es casi inmediatamente visible para el segundo proceso.

La advertencia es que los procesos necesitan 1) sincronizarse, y 2) implementar barreras de memoria para asegurar que el lector no vea datos obsoletos. Pero ambos pueden ser implementados sin llamadas al sistema.

En el lavado, la memoria compartida IPC usando archivos mapeados de memoria >>es<< más rápida que usando archivos o sockets convencionales, y es por eso que la gente lo hace.


También preguntaste implícitamente si la memoria compartida del IPC puede ser implementada sin archivos mapeados en memoria.

  • Una forma práctica sería crear un archivo mapeado de memoria para un archivo que vive en un sistema de archivos de sólo memoria; por ejemplo, un "tmpfs" en Linux.

    Técnicamente, eso sigue siendo un archivo de memoria. Sin embargo, no se incurre en los gastos generales de la descarga de datos al disco, y se evita la posible preocupación por la seguridad de los datos privados del IPC que terminan en el disco.

  • En teoría se podría implementar un segmento compartido entre dos procesos haciendo lo siguiente:

    • En el proceso padre, use mmap para crear un segmento con MAP_ANONYMOUS | MAP_SHARED.
    • Procesos hijo de horquilla. Estos terminarán compartiendo el segmento entre sí y con el proceso padre.

    Sin embargo, implementar eso para un proceso Java sería... un desafío. AFAIK, Java no soporta esto.

Referencia:

Respondida el 31/05/2020 a las 06:17
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more