mmap — Archivos de mapas de memoria

Propósito:Memoria de mapas en lugar de leer los contenidos directamente.

El mapeo de memoria de un archivo usa el sistema de memoria virtual del sistema operativo para acceder a los datos en el sistema de archivos directamente, en lugar de utilizar funciones normales de E/S. El mapeo de memoria típicamente mejora el rendimiento de E/S porque no implica una llamada al sistema por separado para cada acceso y no requiere copiar datos entre buffers – la memoria se accede directamente tanto por el núcleo como por la aplicación de usuario.

Los archivos mapeados en la memoria se pueden tratar como cadenas mutables o como objetos de archivos, según la necesidad. Un archivo mapeado soporta los métodos esperados de la interfaz de archivo, como close(), flush(), read(), readline(), seek(), tell(), y write(). También soporta la interfaz de cadenas de texto, con características tales como rebanar y métodos como find().

Todos los ejemplos utilizan el archivo de texto lorem.txt, que contiene un pedazo de lorem ipsum. Para referencia, el texto del archivo es

lorem.txt
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Donec egestas, enim et consectetuer ullamcorper, lectus ligula rutrum leo,
a elementum elit tortor eu quam. Duis tincidunt nisi ut ante. Nulla
facilisi. Sed tristique eros eu libero. Pellentesque vel
arcu. Vivamus purus orci, iaculis ac, suscipit sit amet, pulvinar eu,
lacus. Praesent placerat tortor sed nisl. Nunc blandit diam egestas
dui. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Aliquam viverra fringilla
leo. Nulla feugiat augue eleifend nulla. Vivamus mauris. Vivamus sed
mauris in nibh placerat egestas. Suspendisse potenti. Mauris
massa. Ut eget velit auctor tortor blandit sollicitudin. Suspendisse
imperdiet justo.

Nota

Hay diferencias en los argumentos y comportamientos para mmap() entre Unix y Windows, que no se discuten por completo aquí. Para más detalles, consulta la documentación de la biblioteca estándar.

Leer

Usa la función mmap() para crear un archivo mapeado en memoria. El primer argumento es un descriptor de archivo, ya sea del método fileno() de un objeto file o de os.open(). El llamador es responsable de abrir el archivo antes de invocar mmap(), y cerrarlo después de que ya no es necesario.

El segundo argumento de mmap() es un tamaño en bytes para la porción del archivo a mapear. Si el valor es 0, el archivo completo es mapeado. Si el tamaño es mayor que el tamaño actual del archivo, el archivo es extendido.

Nota

Windows no admite la creación de un mapeo de longitud cero.

Un argumento opcional de palabra clave, access, es soportado por ambas plataformas. Usa ACCESS_READ para acceso de solo lectura,`` ACCESS_WRITE para escritura directa (las asignaciones a la memoria van directamente al archivo), o ACCESS_COPY para copiar al escribir (las asignaciones a la memoria no se escriben en el archivo).

mmap_read.py
import mmap

with open('lorem.txt', 'r') as f:
    with mmap.mmap(f.fileno(), 0,
                   access=mmap.ACCESS_READ) as m:
        print('First 10 bytes via read :', m.read(10))
        print('First 10 bytes via slice:', m[:10])
        print('2nd   10 bytes via read :', m.read(10))

El puntero del archivo rastrea el último byte al que se accede a través de una operación de segmentación. En este ejemplo, el puntero avanza 10 bytes después de la primera lectura. Luego se restablece al principio del archivo por la operación de segmentación, y se mueve hacia adelante 10 bytes de nuevo por la segmentación. Después de la operación de segmentación, llamando a read() nuevamente da los bytes 11-20 en el archivo.

$ python3 mmap_read.py

First 10 bytes via read : b'Lorem ipsu'
First 10 bytes via slice: b'Lorem ipsu'
2nd   10 bytes via read : b'm dolor si'

Escribir

Para configurar el archivo mapeado en memoria para recibir actualizaciones, comience abriéndolo para adjuntar con el modo 'r+' (no 'w') antes de mapearlo. Luego usa cualquiera de los métodos de la interfaz que cambian los datos (write(), asignación a un segmento, etc.).

El siguiente ejemplo utiliza el modo de acceso predeterminado de ACCESS_WRITE y asignando a un segmento para modificar parte de una línea en su lugar.

mmap_write_slice.py
import mmap
import shutil

# Copy the example file
shutil.copyfile('lorem.txt', 'lorem_copy.txt')

word = b'consectetuer'
reversed = word[::-1]
print('Looking for    :', word)
print('Replacing with :', reversed)

with open('lorem_copy.txt', 'r+') as f:
    with mmap.mmap(f.fileno(), 0) as m:
        print('Before:\n{}'.format(m.readline().rstrip()))
        m.seek(0)  # rewind

        loc = m.find(word)
        m[loc:loc + len(word)] = reversed
        m.flush()

        m.seek(0)  # rewind
        print('After :\n{}'.format(m.readline().rstrip()))

        f.seek(0)  # rewind
        print('File  :\n{}'.format(f.readline().rstrip()))

La palabra «consectetuer» se reemplaza en medio de la primera línea en memoria y en el archivo.

$ python3 mmap_write_slice.py

Looking for    : b'consectetuer'
Replacing with : b'reutetcesnoc'
Before:
b'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.'
After :
b'Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit.'
File  :
Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit.

Modo de Copia

Usando la configuración de acceso ACCESS_COPY no escribe cambios al archivo en disco.

mmap_write_copy.py
import mmap
import shutil

# Copy the example file
shutil.copyfile('lorem.txt', 'lorem_copy.txt')

word = b'consectetuer'
reversed = word[::-1]

with open('lorem_copy.txt', 'r+') as f:
    with mmap.mmap(f.fileno(), 0,
                   access=mmap.ACCESS_COPY) as m:
        print('Memory Before:\n{}'.format(
            m.readline().rstrip()))
        print('File Before  :\n{}\n'.format(
            f.readline().rstrip()))

        m.seek(0)  # rewind
        loc = m.find(word)
        m[loc:loc + len(word)] = reversed

        m.seek(0)  # rewind
        print('Memory After :\n{}'.format(
            m.readline().rstrip()))

        f.seek(0)
        print('File After   :\n{}'.format(
            f.readline().rstrip()))

Es necesario rebobinar el identificador de archivo en este ejemplo por separado desde el identificador mmap, porque el estado interno de los dos objetos se mantienen por separado.

$ python3 mmap_write_copy.py

Memory Before:
b'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.'
File Before  :
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

Memory After :
b'Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit.'
File After   :
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

Expresiones Regulares

Como un archivo mapeado en memoria puede actuar como una cadena, puede usarse con otros módulos que operan en cadenas, tales como expresiones regulares. Este ejemplo encuentra todas las oraciones con «nulla» en ellas.

mmap_regex.py
import mmap
import re

pattern = re.compile(rb'(\.\W+)?([^.]?nulla[^.]*?\.)',
                     re.DOTALL | re.IGNORECASE | re.MULTILINE)

with open('lorem.txt', 'r') as f:
    with mmap.mmap(f.fileno(), 0,
                   access=mmap.ACCESS_READ) as m:
        for match in pattern.findall(m):
            print(match[1].replace(b'\n', b' '))

Debido a que el patrón incluye dos grupos, el valor de retorno de findall() es una secuencia de tuplas. La declaración print saca la oración correspondiente y reemplaza las líneas nuevas con espacios para que cada resultado se imprima en una sola línea.

$ python3 mmap_regex.py

b'Nulla facilisi.'
b'Nulla feugiat augue eleifend nulla.'

Ver también