Rapidez de escritura en archivos: "write" contra "mmap"

20200526173139

Rapidez de escritura en archivos: "write" contra "mmap"


Este es un experimento para comparar la velocidad de dos métodos para escribir archivos: mediante la función write, y mapeando el archivo en la memoria de la aplicación. La segunda podemos hacerla con alguna de las funciones para escribir en memoria (como mset, o mcpy) sobre algún archivo mapeado en la memoria de la aplicación con ayuda de mmap.
El experimento consiste en crear dos programas que escriban un archivo de un tamaño arbitrario llenos con el caracter 0x00 (caracter nulo), cada programa con uno de los dos métodos mencionados en el párrafo anterior. Los programas toman dos parámetros de entrada: el nombre del archivo que van a escribir, y la cantidad de mega-bytes que van a escribir en los archivos.

La aplicación con write

El listado 1 contiene nuestra aplicación que utiliza la función write para escribir el archivo. Llamamos a este archivo write_performance.c. Su primer argumento de la línea de comandos debe ser la ruta del archivo que va a escribir, tal como lo pide el prototipo de la función open, y el segundo argumento debe ser un número entero que indique cuántos mega-bytes escribirá sobre el archivo.
Para crear el arreglo de caracteres nulos, empleamos la función calloc. Y para asegurar que la escritura al archivo haya terminado antes de que finalice el programa, ejecutaremos fsync.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

int main(int nargs, char ** argv)
{
    int target_file;
    char * buffer;

    target_file = open(
        argv[1],
        O_RDWR | O_CREAT,
        0666
    );

    buffer = calloc(
        atoi(argv[2]) * 1024 * 1024,
        1
    );


    write(
        target_file,
        buffer,
        atoi(argv[2]) * 1024 * 1024
    );

    close(target_file);

    return 0;
}
Listado 1. Contenido de write_performance.c.

La aplicación con MMAP

El listado 2 contiene nuestra versión del programa que escribe sobre el archivo ahora con funciones que manipulan un espacio de memoria mapeado en el programa.
Para asegurarnos que el archivo tiene el tamaño deseado antes de mapearlo en memoria, lo abrimos y ejecutamos lseek para colocar el cursor en la última posición del archivo. Una vez en esa posición, ahí escribimos un caracter nulo (y en consecuencia, ahora el archivo tiene el tamaño deseado).
Después, mapeamos el archivo en memoria con mmap y lo llenamos de caracteres nulos utilizando la función mset. Para asegurar que las escrituras hayan llegado al archivo antes de que termine el programa, ejecutamos la función msync. Finalmente, eliminamos el mapa de memoria con munmap y cerramos el archivo.
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main(int nargs, char ** argv)
{
    int target_file;
    char * map;

    target_file = open(
        argv[1],
        O_RDWR | O_CREAT,
        0666
    );

    lseek(
        target_file,
        atoi(argv[2]) * 1024 * 1024,
        SEEK_SET
    );

    write(
        target_file,
        "",
        1
    );

    map = mmap(
        0,
        atoi(argv[2]) * 1024 * 1024,
        PROT_READ | PROT_WRITE,
        MAP_SHARED,
        target_file,
        0
    );

    memset(
        map, 
        0x00, 
        atoi(argv[2]) * 1024 * 1024
    );

    msync(
        map, 
        atoi(argv[2]) * 1024 * 1024,
        MS_SYNC
    );

    munmap(
        map,
        atoi(argv[2]) * 1024 * 1024
    );

    close(target_file);

    return 0;
}
Listado 2. Contenido de mmap_performance.c

La prueba

Para compilar nuestras aplicaciones, utilizamos los comandos de consola que aparecen en el listado 3. Y para hacer las mediciones de desempeño, ejecutamos los programas dentro del contexto del comando time (como lo mostramos en el listado 4).
El resultado final muestra que la versión de mapeo en memoria es más rápida que la versión que utiliza la función write. El resultado puede variar en cada instancia del proceso. Pero el desempeño de mmap_performance se mantiene por arriba de write_performance en aproximadamente 10%.
$ gcc ./write_performance.c -o write_performance
$ gcc ./mmap_performance.c -o mmap_performance
Listado 3. Comandos para compilar las aplicaciones write_performance y mmap_performance.
$ time ./write_performance test_file 1000

real 0m1.343s
user 0m0.106s
sys 0m0.938s

$ time ./mmap_performance testfile 1000

real 0m1.150s
user 0m0.197s
sys 0m0.767s
Listado 4. Medidas de desempeño de los programas write_performance y mmap_performance al escribir archivos de 1 GB.

Comentarios

Entradas más populares de este blog

10 palabras valen más que una imagen - 20240526223027

20220214085408 - El reto de hacer amistades nuevas en la vida adulta

20220208192042 - Los modelos abstractos: sólo una cara del diamante