language agnostic - ¿Cuáles son las ventajas de los archivos mapeados en memoria?
language-agnostic memory (4)
He estado investigando archivos mapeados en memoria para un proyecto y agradecería cualquier pensamiento de personas que los hayan usado anteriormente o hayan decidido no usarlos, y ¿por qué?
En particular, me preocupan los siguientes, en orden de importancia:
- concurrencia
- acceso aleatorio
- actuación
- facilidad de uso
- portabilidad
Creo que la ventaja es que se reduce la cantidad de copia de datos requerida sobre los métodos tradicionales de lectura de un archivo.
Si su aplicación puede usar los datos "en su lugar" en un archivo mapeado en memoria, puede entrar sin copiarse; si utiliza una llamada al sistema (por ejemplo, pread () de Linux), eso normalmente implica que el kernel copie los datos de sus propios búferes en espacio de usuario. Esta copia adicional no solo lleva tiempo, sino que también disminuye la efectividad de las memorias caché de la CPU al acceder a esta copia adicional de los datos.
Si los datos realmente tienen que leerse desde el disco (como en la E / S física), entonces el sistema operativo aún tiene que leerlos, un error de página probablemente no sea mejor en cuanto a rendimiento que una llamada al sistema, pero si no (es decir, ya en la memoria caché del sistema operativo), el rendimiento en teoría debería ser mucho mejor.
En el lado negativo, no hay una interfaz asíncrona para los archivos mapeados en la memoria: si intenta acceder a una página que no está mapeada, genera un error de página y luego hace que el subproceso espere la E / S.
La desventaja obvia de los archivos mapeados en memoria es en un sistema operativo de 32 bits: puede agotar fácilmente el espacio de direcciones.
He utilizado un archivo mapeado en memoria para implementar una función ''autocompletar'' mientras el usuario escribe. Tengo más de 1 millón de números de pieza de producto almacenados en un solo archivo de índice. El archivo tiene cierta información de encabezado típica, pero la mayor parte del archivo es una matriz gigante de registros de tamaño fijo ordenados en el campo clave.
En tiempo de ejecución, el archivo se correlaciona con la memoria, se transfiere a una matriz de struct
estilo C
, y hacemos una búsqueda binaria para encontrar números de pieza coincidentes a medida que el usuario escribe. Solo unas pocas páginas de memoria del archivo se leen realmente desde el disco, independientemente de las páginas que se golpeen durante la búsqueda binaria.
- Concurrencia: tuve un problema de implementación en el que a veces la memoria asignaba el archivo varias veces en el mismo espacio de proceso. Esto fue un problema, como recuerdo, porque a veces el sistema no podía encontrar un bloque libre de memoria virtual suficientemente grande para mapear el archivo. La solución fue solo asignar el archivo una vez y analizar todas las llamadas a él. En retrospectiva, usar un servicio completo de Windows hubiera sido genial.
- Acceso aleatorio: la búsqueda binaria es, sin duda, de acceso aleatorio y rápida como un rayo
- Rendimiento: la búsqueda es extremadamente rápida. A medida que los usuarios escriben, una ventana emergente muestra una lista de los números de parte del producto que coinciden, la lista se reduce a medida que continúan escribiendo. No hay un retraso notable al escribir.
La concurrencia sería un problema. El acceso aleatorio es más fácil El rendimiento es bueno o excelente. Facilidad de uso. No tan bueno. Portabilidad: no tan caliente.
Los usé en un sistema Sun hace mucho tiempo, y esos son mis pensamientos.
Los archivos mapeados en memoria se pueden usar para reemplazar el acceso de lectura / escritura o para admitir el uso compartido simultáneo. Cuando los utilizas para un mecanismo, también obtienes el otro.
En lugar de buscar, escribir y leer todo en un archivo, lo mapea en la memoria y simplemente accede a los bits donde espera que estén.
Esto puede ser muy útil, y dependiendo de la interfaz de memoria virtual puede mejorar el rendimiento. La mejora del rendimiento puede ocurrir porque el sistema operativo ahora puede administrar esta antigua "E / S de archivos" junto con todos sus otros accesos de memoria programática, y puede (en teoría) aprovechar los algoritmos de paginación y demás que ya está utilizando para soportar memoria virtual para el resto de su programa. Sin embargo, depende de la calidad de su sistema de memoria virtual subyacente. Las anécdotas que he escuchado dicen que los sistemas de memoria virtual Solaris y * BSD pueden mostrar mejores mejoras de rendimiento que el sistema VM de Linux, pero no tengo datos empíricos que respalden esto. YMMV.
La simultaneidad entra en escena cuando se considera la posibilidad de múltiples procesos usando el mismo "archivo" a través de la memoria mapeada. En el modelo de lectura / escritura, si dos procesos escribieron en la misma área del archivo, podría estar bastante seguro de que uno de los datos del proceso llegaría al archivo, sobrescribiendo los datos del otro proceso. Tendrían uno, o el otro, pero no una mezcla extraña. Tengo que admitir que no estoy seguro de si este es el comportamiento exigido por cualquier norma, pero es algo en lo que podría confiar. (¡En realidad es una buena pregunta de seguimiento!)
En el mundo mapeado, en cambio, imagina dos procesos tanto "escritos". Lo hacen haciendo "almacenes de memoria", lo que hace que el O / S pagine los datos en el disco, eventualmente. Pero mientras tanto, se puede esperar que ocurran escrituras superpuestas.
Aquí hay un ejemplo. Digamos que tengo dos procesos que escriben 8 bytes en el desplazamiento 1024. El proceso 1 está escribiendo ''11111111'' y el proceso 2 está escribiendo ''22222222''. Si usan E / S de archivos, entonces pueden imaginar, en el fondo de las O / S, que hay un búfer lleno de 1 y un búfer lleno de 2, ambos dirigidos al mismo lugar en el disco. Uno de ellos llegará primero y el otro segundo. En este caso, el segundo gana. Sin embargo , si uso el enfoque de archivo mapeado en memoria, el proceso 1 irá a un almacén de memoria de 4 bytes, seguido de otro almacén de memoria de 4 bytes (supongamos que es el tamaño máximo de almacenamiento de memoria). El proceso 2 hará lo mismo. En función de cuándo se ejecutan los procesos, puede esperar ver cualquiera de los siguientes:
11111111
22222222
11112222
22221111
La solución a esto es usar la exclusión mutua explícita, que probablemente sea una buena idea en cualquier caso. En cierto modo, confiabas en que el O / S hiciera "lo correcto" en el caso de E / S del archivo de lectura / escritura.
La primitiva de exclusión mutua de clasificación es el mutex. Para los archivos mapeados en memoria, le sugiero que mire un mutex mapeado en memoria, disponible usando (p. Ej.) Pthread_mutex_init ().
Edite con un gotcha: cuando está usando archivos mapeados, existe la tentación de insertar punteros a los datos en el archivo, en el archivo en sí (lista de enlaces cruzados almacenada en el archivo asignado). No desea hacer eso, ya que el archivo puede mapearse en diferentes direcciones absolutas en diferentes momentos o en diferentes procesos. En su lugar, use compensaciones dentro del archivo asignado.