rendimiento - Python, ¿por qué mmap.move() llena la memoria?
editar: Usando Win10 y python 3.5
Tengo una función que usa mmap para eliminar bytes de un archivo en un desplazamiento determinado:
def delete_bytes(fobj, offset, size):
fobj.seek(0, 2)
filesize = fobj.tell()
move_size = filesize - offset - size
fobj.flush()
file_map = mmap.mmap(fobj.fileno(), filesize)
file_map.move(offset, offset + size, move_size)
file_map.close()
fobj.truncate(filesize - size)
fobj.flush()
Funciona súper rápido, pero cuando lo ejecuto en una gran cantidad de archivos, la memoria se llena rápidamente y mi sistema deja de responder.
Después de experimentar un poco, descubrí que el método move() era el culpable aquí y, en particular, la cantidad de datos que se movían (move_size). La cantidad de memoria que se utiliza es equivalente a la total cantidad de datos que mmap.move() mueve. Si tengo 100 archivos con cada movimiento de ~30 MB, la memoria se llena con ~3 GB.
¿Por qué los datos movidos no se liberan de la memoria?
Cosas que probé que no surtieron efecto:
- llamar a gc.collect() al final de la función.
- reescribiendo la función para que se mueva en pequeños fragmentos.
------------Respuesta------------
Parece que debería funcionar. Encontré un bit sospechoso en mmapmodule.c scódigo fuente, #ifdef MS_WINDOWS. Específicamente, después de toda la configuración para analizar argumentos, el código hace esto:
if (fileno != -1 && fileno != 0) {
/* Ensure that fileno is within the CRT's valid range */
if (_PyVerify_fd(fileno) == 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
fh = (HANDLE)_get_osfhandle(fileno);
if (fh==(HANDLE)-1) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
/* Win9x appears to need us seeked to zero */
lseek(fileno, 0, SEEK_SET);
}
que mueve el desplazamiento de su objeto de archivo subyacente de "fin de archivo" a "inicio de archivo" y luego lo deja allí. Parece que no debería romper nada, pero podría valer la pena hacer su propia búsqueda de inicio de archivo justo antes de llamar a mmap.mmap para mapear el archivo.
(Todo a continuación está mal, pero se dejó porque hay comentarios al respecto).
En general, después de usar mmap(), debe usar munmap() para deshacer el mapeo. Simplemente cerrar el descriptor de archivo no tiene ningún efecto. La documentación de Linux menciona esto explícitamente:
munmap()
La llamada al sistema munmap() elimina las asignaciones para el rango de direcciones especificado y hace que más referencias a direcciones dentro del rango generen referencias de memoria no válidas. La región también se desasigna automáticamente cuando finaliza el proceso. Por otra parte, cerrar elel descriptor de archivo no desasigna la región.
(La documentación de BSD es similar. Windows puede comportarse de manera diferente a los sistemas similares a Unix aquí, pero lo que está viendo sugiere que funcionan de la misma manera).
Desafortunadamente, el módulo mmap de Python no vincula la llamada al sistema munmap (ni mprotect), al menos a partir de la 2.7.11 y la 3.4.4. Como solución alternativa, puede utilizar el módulo ctypes. Vea esta pregunta para ver un ejemplo (llama a reiniciar pero la misma técnica funciona para todas las bibliotecas Cfunciones aleatorias). O, para un método algo más agradable, puede escribir envoltorios en cython.
Etiquetas: mmap memory performance python
Artículos relacionados:
servicios web: cómo extraer/descifrar el tiempo de caducidad del token web Json emitido externamente
javascript - formulario de botón de radio sin enviar
- Regex - Área de captura de Javascript
- c#: la API web ignora la propiedad original al serializar el objeto heredado de DynamicObject
- php - Magento 1.8 - Actualizar Mini Cart después de AJAX POST
- falla de segmentación: cómo asignar memoria para una matriz 3D usando calloc en c ++
- php: la misma página con 2 URL .htaccess sin redirección
- javascript: ¿cómo se restablece un "$ tiempo de espera" y se desactiva un "$ reloj ()"?
- c++ - Almacenamiento y recuperación de datos en bases de datos relacionales
- html - Metaetiqueta de ventana gráfica en css
- c# - La validación del cuadro de texto funciona según el valor desplegable
- localhost - Solucionar problemas de dial tcp
- Java: ¿cómo crear un comando de consola en la aplicación web Spring Boot usando Spring Shell?
- Decodificar UTF-8 String Swift iOS
- java - Android - carga dinámicamente la clase en la memoria
- mysql: ¿por qué obtengo un resultado que consiste en más de un error de fila, cuando finalmente se devuelve una fila?
- DataTables - Ubicación del archivo
Artículos calientes
- medicina china para la bronquitis
- Diferencia entre infección de las vías respiratorias superiores y bronquitis
- ¿Qué causa la epididimitis?
- Criterios diagnósticos de cálculos renales
- ¿Qué medicamento la masturbación causa prostatitis?
- ¿Cuáles son los factores de padecer gota?
- Qué comer durante la convalecencia de una hemorragia cerebral
- que es el alzheimer
- Principales criterios diagnósticos de las bronquiectasias
- ¿Cuáles son las formas de prevenir y tratar la menstruación irregular en las mujeres?
- Síntomas de gota de ternera
- Saco gestacional de 5x4mm cuanto dura el embarazo
- La diferencia entre espinillas y espinillas de ácaros
- Por qué frotar con jengibre puede tratar la caída del cabello
- ¿Cuánto tiempo se puede vivir con insuficiencia hepática?