Memcpy toma el mismo tiempo que memset
Quiero medir el ancho de banda de la memoria usandomemcpy
. Modifiqué el código de esta respuesta:por qué vectorizar el bucle no mejora el rendimiento que usómemset
para medir el ancho de banda. El problema es esememcpy
es solo un poco más lento quememset
cuando espero que sea aproximadamente dos veces más lento ya que funciona con el doble de memoria.
Más específicamente, ejecuto más de 1 GB de matricesa
yb
(voluntad asignadacalloc
) 100 veces con las siguientes operaciones.
operation time(s)
-----------------------------
memset(a,0xff,LEN) 3.7
memcpy(a,b,LEN) 3.9
a[j] += b[j] 9.4
memcpy(a,b,LEN) 3.8
Darse cuenta dememcpy
es solo un poco más lento quememset
. Las operacionesa[j] += b[j]
(dóndej
va sobre[0,LEN)
) debería tardar tres veces más quememcpy
porque opera con el triple de datos. Sin embargo, solo es aproximadamente 2.5 tan lento comomemset
.
Entonces inicialicéb
a cero conmemset(b,0,LEN)
y prueba de nuevo:
operation time(s)
-----------------------------
memcpy(a,b,LEN) 8.2
a[j] += b[j] 11.5
Ahora vemos quememcpy
es aproximadamente el doble de lento quememset
ya[j] += b[j]
es aproximadamente tres veces más lento quememset
como espero
Por lo menos, hubiera esperado eso antesmemset(b,0,LEN)
esememcpy
seríamás lento debido a la asignación diferida (primer toque) en la primera de las 100 iteraciones.
¿Por qué solo obtengo el tiempo que espero despuésmemset(b,0,LEN)
?
prueba.c
#include <time.h>
#include <string.h>
#include <stdio.h>
void tests(char *a, char *b, const int LEN){
clock_t time0, time1;
time0 = clock();
for (int i = 0; i < 100; i++) memset(a,0xff,LEN);
time1 = clock();
printf("%f\n", (double)(time1 - time0) / CLOCKS_PER_SEC);
time0 = clock();
for (int i = 0; i < 100; i++) memcpy(a,b,LEN);
time1 = clock();
printf("%f\n", (double)(time1 - time0) / CLOCKS_PER_SEC);
time0 = clock();
for (int i = 0; i < 100; i++) for(int j=0; j<LEN; j++) a[j] += b[j];
time1 = clock();
printf("%f\n", (double)(time1 - time0) / CLOCKS_PER_SEC);
time0 = clock();
for (int i = 0; i < 100; i++) memcpy(a,b,LEN);
time1 = clock();
printf("%f\n", (double)(time1 - time0) / CLOCKS_PER_SEC);
memset(b,0,LEN);
time0 = clock();
for (int i = 0; i < 100; i++) memcpy(a,b,LEN);
time1 = clock();
printf("%f\n", (double)(time1 - time0) / CLOCKS_PER_SEC);
time0 = clock();
for (int i = 0; i < 100; i++) for(int j=0; j<LEN; j++) a[j] += b[j];
time1 = clock();
printf("%f\n", (double)(time1 - time0) / CLOCKS_PER_SEC);
}
C Principal
#include <stdlib.h>
int tests(char *a, char *b, const int LEN);
int main(void) {
const int LEN = 1 << 30; // 1GB
char *a = (char*)calloc(LEN,1);
char *b = (char*)calloc(LEN,1);
tests(a, b, LEN);
}
Compilar con (gcc 6.2)gcc -O3 test.c main.c
. Clang 3.8 da esencialmente el mismo resultado.
Sistema de prueba: [email protected] (Skylake), 32 GB DDR4, Ubuntu 16.10. En mi sistema Haswell, los anchos de banda tienen sentido antesmemset(b,0,LEN)
es decir, solo veo un problema en mi sistema Skylake.
Descubrí este problema por primera vez desdea[j] += b[k]
operacionesen esta respuesta que estaba sobreestimando el ancho de banda.
Se me ocurrió una prueba más simple
#include <time.h>
#include <string.h>
#include <stdio.h>
void __attribute__ ((noinline)) foo(char *a, char *b, const int LEN) {
for (int i = 0; i < 100; i++) for(int j=0; j<LEN; j++) a[j] += b[j];
}
void tests(char *a, char *b, const int LEN) {
foo(a, b, LEN);
memset(b,0,LEN);
foo(a, b, LEN);
}
Esto da salida.
9.472976
12.728426
Sin embargo, si lo hagomemset(b,1,LEN)
en principal despuéscalloc
(ver abajo) luego sale
12.5
12.5
Esto me lleva a pensar que este es un problema de asignación del sistema operativo y no un problema del compilador.
#include <stdlib.h>
int tests(char *a, char *b, const int LEN);
int main(void) {
const int LEN = 1 << 30; // 1GB
char *a = (char*)calloc(LEN,1);
char *b = (char*)calloc(LEN,1);
//GCC optimizes memset(b,0,LEN) away after calloc but Clang does not.
memset(b,1,LEN);
tests(a, b, LEN);
}