Je veux mesurer la bande passante mémoire en utilisant memcpy
. J'ai modifié le code de cette réponse : pourquoi la vectorisation de la boucle n'améliore pas les performances qui utilisait memset
pour mesurer la bande passante. Le problème est que memcpy
est seulement légèrement plus lent que memset
alors que je m'attendais à ce qu'il soit environ deux fois plus lent car il opère sur deux fois la mémoire.
Plus spécifiquement, je parcours des tableaux de 1 Go, a
et b
(alloués avec calloc
), 100 fois avec les opérations suivantes.
opération temps(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
Remarquez que memcpy
est seulement légèrement plus lent que memset
. Les opérations a[j] += b[j]
(où j
va de [0,LEN)
) devraient prendre trois fois plus de temps que memcpy
car elles opèrent sur trois fois plus de données. Cependant, elles sont seulement environ 2,5 fois plus lentes que memset
.
Ensuite, j'ai initialisé b
à zéro avec memset(b,0,LEN)
et j'ai retesté :
opération temps(s)
-----------------------------
memcpy(a,b,LEN) 8.2
a[j] += b[j] 11.5
Maintenant, on observe que memcpy
est environ deux fois plus lent que memset
et a[j] += b[j]
est environ trois fois plus lent que memset
comme je m'y attendais.
Je m'attendais au minimum à ce que avant memset(b,0,LEN)
que memcpy
serait plus lent en raison de l'allocation paresseuse (first touch) lors de la première des 100 itérations.
Pourquoi n'obtiens-je la durée attendue qu'après memset(b,0,LEN)
?
test.c
#include
#include
#include
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
#include
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);
}
Compiler avec (gcc 6.2) `gcc -O3 test.c main.c`. Clang 3.8 donne essentiellement le même résultat.
Système de test : i7-6700HQ@2,60 GHz (Skylake), 32 Go DDR4, Ubuntu 16.10. Sur mon système Haswell, les bandes passantes ont du sens avant `memset(b,0,LEN)`, c'est-à-dire que je ne rencontre de problème que sur mon système Skylake.
J'ai découvert ce problème lors des opérations `a[j] += b[k]` [dans cette réponse](https://stackoverflow.com/questions/42964820/why-is-this-simd-multiplication-not-faster-than-non-simd-multiplication/42972674#42972674) qui surestimait la bande passante.
------
J'ai imaginé un test plus simple
#include
#include
#include
void __attribute__ ((noinline)) foo(char *a, char *b, const int LEN) {
for (int i = 0; i < 100; i++) for(int j=0; j
``Cela affiche.
9,472976
12,728426
Cependant, si je fais `memset(b,1,LEN)` dans main après `calloc` (voir ci-dessous) alors cela affiche
12,5
12,5
Cela me conduit à penser qu'il s'agit d'un problème d'allocation du système d'exploitation et non d'un problème de compilateur.
#include
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 optimise memset(b,0,LEN) après calloc mais Clang ne le fait pas.
memset(b,1,LEN);
tests(a, b, LEN);
}`` ```