Le moyen le plus rapide de mettre à zéro un tableau 2D en C?

Je veux zéro à plusieurs resockets un grand tableau 2D en C. C’est ce que je fais en ce moment:

// Array of size n * m, where n may not equal m for(j = 0; j < n; j++) { for(i = 0; i < m; i++) { array[i][j] = 0; } } 

J’ai essayé d’utiliser memset:

 memset(array, 0, sizeof(array)) 

Mais cela ne fonctionne que pour les tableaux 1D. Lorsque j’imprime le contenu du tableau 2D, la première rangée est zéros, mais j’ai ensuite reçu un grand nombre de nombres aléatoires et cela se bloque.

 memset(array, 0, sizeof(array[0][0]) * m * n); 

m et n sont la largeur et la hauteur du tableau à deux dimensions (dans votre exemple, vous avez un tableau à deux dimensions carré, donc m == n ).

Si array est vraiment un tableau, alors vous pouvez le “zéro” avec:

 memset(array, 0, sizeof array); 

Mais il y a deux points à connaître:

  • cela ne fonctionne que si le array est réellement un “tableau à deux dimensions”, c’est-à-dire qu’il a été déclaré T array[M][N]; pour certains type T
  • cela ne fonctionne que dans la scope où le array été déclaré. Si vous le transmettez à une fonction, le array noms se désintègre en un pointeur et sizeof ne vous donnera pas la taille du tableau.

Faisons une expérience:

 #include  void f(int (*arr)[5]) { printf("f: sizeof arr: %zu\n", sizeof arr); printf("f: sizeof arr[0]: %zu\n", sizeof arr[0]); printf("f: sizeof arr[0][0]: %zu\n", sizeof arr[0][0]); } int main(void) { int arr[10][5]; printf("main: sizeof arr: %zu\n", sizeof arr); printf("main: sizeof arr[0]: %zu\n", sizeof arr[0]); printf("main: sizeof arr[0][0]: %zu\n\n", sizeof arr[0][0]); f(arr); return 0; } 

Sur ma machine, les impressions ci-dessus:

 main: sizeof arr: 200 main: sizeof arr[0]: 20 main: sizeof arr[0][0]: 4 f: sizeof arr: 8 f: sizeof arr[0]: 20 f: sizeof arr[0][0]: 4 

Même si arr est un tableau, il se désintègre en un pointeur vers son premier élément lorsqu’il est passé à f() , et donc les tailles imprimées dans f() sont “incorrectes”. De plus, dans f() la taille de arr[0] est la taille du tableau arr[0] , qui est un “tableau [5] de int “. Ce n’est pas la taille d’un int * , car le “decaying” ne se produit qu’au premier niveau, et c’est pourquoi nous devons déclarer f() comme prenant un pointeur sur un tableau de la taille correcte.

Donc, comme je l’ai dit, ce que vous faisiez à l’origine ne fonctionnera que si les deux conditions ci-dessus sont remplies. Sinon, vous devrez faire ce que les autres ont dit:

 memset(array, 0, m*n*sizeof array[0][0]); 

Enfin, memset() et la boucle for vous avez affichée ne sont pas équivalentes au sens ssortingct. Il pourrait y avoir (et il y a eu) des compilateurs où “tous les bits nuls” n’est pas égal à zéro pour certains types, tels que les pointeurs et les valeurs à virgule flottante. Je doute que vous deviez vous inquiéter à ce sujet.

Eh bien, le moyen le plus rapide de le faire est de ne pas le faire du tout.

Cela semble étrange, je sais, voici un pseudocode:

 int array [][]; bool array_is_empty; void ClearArray () { array_is_empty = true; } int ReadValue (int x, int y) { return array_is_empty ? 0 : array [x][y]; } void SetValue (int x, int y, int value) { if (array_is_empty) { memset (array, 0, number of byte the array uses); array_is_empty = false; } array [x][y] = value; } 

En fait, il efface toujours le tableau, mais seulement quand quelque chose est écrit dans le tableau. Ce n’est pas un gros avantage ici. Cependant, si le tableau 2D a été implémenté en utilisant, par exemple, un arbre quadruple (pas un esprit dynamic) ou une collection de lignes de données, vous pouvez localiser l’effet de l’indicateur booléen, mais vous aurez besoin de plus de drapeaux. Dans l’arbre quadratique, définissez simplement le drapeau vide pour le nœud racine, dans le tableau de lignes, définissez simplement le drapeau pour chaque ligne.

Ce qui conduit à la question “pourquoi voulez-vous zéro à plusieurs resockets un grand tableau 2D”? A quoi sert le tableau? Y a-t-il un moyen de changer le code pour que le tableau n’ait pas besoin d’être mis à zéro?

Par exemple, si vous aviez:

 clear array for each set of data for each element in data set array += element 

c’est-à-dire, utilisez-le pour un tampon d’accumulation, puis le changer comme cela améliorerait la performance sans fin:

  for set 0 and set 1 for each element in each set array = element1 + element2 for remaining data sets for each element in data set array += element 

Cela ne nécessite pas que le tableau soit effacé mais fonctionne toujours. Et ce sera beaucoup plus rapide que de vider le tableau. Comme je l’ai dit, le moyen le plus rapide est de ne pas le faire en premier lieu.

Si vous êtes vraiment obsédé par la vitesse (et pas tellement par la portabilité), je pense que le moyen le plus rapide de le faire serait d’utiliser les valeurs insortingnsèques de SIMD. Par exemple, sur les processeurs Intel, vous pouvez utiliser ces instructions SSE2:

 __m128i _mm_setzero_si128 (); // Create a quadword with a value of 0. void _mm_storeu_si128 (__m128i *p, __m128i a); // Write a quadword to the specified address. 

Chaque instruction de magasin définira quatre valeurs de 32 bits à zéro en une seule fois.

p doit être aligné sur 16 octets, mais cette ressortingction est également bonne pour la vitesse car elle aidera le cache. L’autre ressortingction est que p doit pointer vers une taille d’allocation multiple de 16 octets, mais c’est cool aussi car cela nous permet de dérouler facilement la boucle.

Ayez ceci dans une boucle, et déroulez la boucle quelques fois, et vous aurez un initialisateur rapide fou:

 // Assumes int is 32-bits. const int mr = roundUpToNearestMultiple(m, 4); // This isn't the optimal modification of m and n, but done this way here for clarity. const int nr = roundUpToNearestMultiple(n, 4); int i = 0; int array[mr][nr] __atsortingbute__ ((aligned (16))); // GCC directive. __m128i* px = (__m128i*)array; const int incr = s >> 2; // Unroll it 4 times. const __m128i zero128 = _mm_setzero_si128(); for(i = 0; i < s; i += incr) { _mm_storeu_si128(px++, zero128); _mm_storeu_si128(px++, zero128); _mm_storeu_si128(px++, zero128); _mm_storeu_si128(px++, zero128); } 

Il existe également une variante de _mm_storeu qui contourne le cache (la mise à zéro du tableau ne pollue pas le cache), ce qui peut vous apporter des avantages de performance secondaires dans certaines circonstances.

Voir ici pour la référence SSE2: http://msdn.microsoft.com/en-us/library/kcwz153a(v=vs.80).aspx

Si vous initialisez le tableau avec malloc , utilisez plutôt calloc ; il va mettre à zéro votre tableau gratuitement. (Même perf évidemment que memset, juste moins de code pour vous.)

int array[N][M] = {0};

… au moins dans GCC 4.8.

Comment votre tableau 2D a-t-il été déclaré?

Si c’est quelque chose comme:

 int arr[20][30]; 

Vous pouvez le mettre à zéro en faisant:

 memset(arr, sizeof(int)*20*30); 

Utilisez calloc au lieu de malloc. calloc initiera tous les champs à 0.

int * a = (int *) calloc (n, taille de (int));

// toutes les cellules d’un ont été initialisées à 0