Rendimiento: conjunto de memoria

4 minutos de lectura

Rendimiento conjunto de memoria
JB_Usuario

Tengo un código C simple que hace esto (pseudocódigo):

#define N 100000000
int *DataSrc = (int *) malloc(N);
int *DataDest = (int *) malloc(N);
memset(DataSrc, 0, N);
for (int i = 0 ; i < 4 ; i++) {
    StartTimer();
    memcpy(DataDest, DataSrc, N);
    StopTimer();
}
printf("%d\n", DataDest[RandomInteger]);

Mi PC: Intel Core i7-3930, con memoria DDR3 1600 de 4×4 GB con RedHat 6.1 de 64 bits.

El primero memcpy() ocurre a 1,9 GB/s, mientras que los tres siguientes ocurren a 6,2 GB/s. El tamaño del búfer (N) es demasiado grande para que esto sea causado por efectos de caché. Entonces, mi primera pregunta:

  • ¿Por qué es el primero memcpy() mucho más lento? Quizás malloc() no asigna completamente la memoria hasta que la usa?

Si elimino la memset()entonces el primero memcpy() funciona a aproximadamente 1,5 GB/seg, pero los tres siguientes funcionan a 11,8 GB/seg. Casi 2x de aceleración. Mi segunda pregunta:

  • Por que es memcpy() 2 veces más rápido si no llamo memset()?

  • ¿No es UB si memcpy desde una fuente no inicializada? ¿Qué compilador estás usando con qué optimizaciones? Haga que los tiempos sean más confiables aumentando el tamaño de los datos en 10x o más.

    – usuario

    18 mayo 2014 a las 14:53


  • @usr Los datos serán aleatorios, no hay ub siempre que no use los datos de una manera que pueda introducir ub. No hay código en el ejemplo que haría eso.

    – esta

    18 mayo 2014 a las 15:00


  • Por cierto: la velocidad de bus de 11,8 GB/s me parece demasiado rápida.

    – salvaje

    18 mayo 2014 a las 15:01

  • @usr Leer la variable no inicializada no activa ub, usar ese valor incorrectamente lo hace. Por ejemplo, usar ese valor para acceder a un desplazamiento de matriz activará ub. Supongo que técnicamente (estándar) tienes razón.

    – esta

    18 mayo 2014 a las 15:03


  • Eso puede ser correcto, pero el OP menciona específicamente gcc y linux. Además: no hay representaciones trampa posibles para ints (y los ints nunca se usan, solo se copian). De lo contrario, leer datos aleatorios de un archivo de disco desconocido también podría causar problemas.

    – salvaje

    18 mayo 2014 a las 15:08


Como otros ya señalaron, Linux usa un estrategia de asignación de memoria optimista.

La diferencia entre el primero y el siguiente. memcpys es la inicialización de DataDest.

Como ya has visto, cuando eliminas memset(DataSrc, 0, N)el primero memcpy es aún más lento, porque también se deben asignar las páginas para la fuente. Cuando inicializas ambos, DataSrc y DataDestp.ej

memset(DataSrc, 0, N);
memset(DataDest, 0, N);

todos memcpys se ejecutará aproximadamente a la misma velocidad.

Para la segunda pregunta: cuando inicializa la memoria asignada con memset todas las páginas se distribuirán consecutivamente. Por otro lado, cuando la memoria se asigna a medida que copia, las páginas de origen y destino se asignarán intercaladas, lo que podría marcar la diferencia.

  • ¡Fantástica respuesta @Olaf Dietsche!

    –Deepak Tatyaji Ahire

    27 de marzo de 2021 a las 12:37

1647676206 798 Rendimiento conjunto de memoria
pablo r

Lo más probable es que esto se deba a una asignación diferida en el subsistema de su máquina virtual. Por lo general, cuando asigna una gran cantidad de memoria, solo las primeras N páginas se asignan y conectan a la memoria física. Cuando accede más allá de estas primeras N páginas, se generan fallas de página y se asignan y conectan más páginas “bajo demanda”.

En cuanto a la segunda parte de la pregunta, creo que algunas implementaciones de VM en realidad rastrean las páginas puestas a cero y las manejan de manera especial. Intenta inicializar DataSrc a los valores reales (por ejemplo, aleatorios) y repita la prueba.

  • +1 – ‘Ensuciar’ (escribir en) todas las páginas de antemano debería aclarar las cosas, uno puede intentar calloc(): stackoverflow.com/q/1538420/1175253

    – sam

    18 mayo 2014 a las 15:10


  • @Sam: la respuesta principal en esa pregunta vinculada era incorrecta hasta que la arreglé; calloc en la mayoría de los sistemas operativos principales, las páginas del kernel se ponen a cero, por lo que aún se asignan de forma perezosa y fallarán en la página al leer o escribir.

    – Peter Cordes

    19 de febrero de 2020 a las 6:49

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad