web-dev-qa-db-fra.com

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

Je veux mettre à zéro à plusieurs reprises un grand tableau 2d en C. Voici 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 ligne est constituée de zéros, mais j'ai alors une charge de grands nombres aléatoires et elle se bloque.

85
Eddy
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 carré à deux dimensions, donc m == n).

164
James McNellis

Si array est vraiment un tableau, vous pouvez le "mettre à zéro" avec:

memset(array, 0, sizeof array);

Mais il y a deux points que vous devez savoir:

  • cela ne fonctionne que si array est vraiment un "tableau à deux dimensions", c’est-à-dire qu’il a été déclaré T array[M][N]; pour un type T.
  • cela ne fonctionne que dans la portée où array a été déclaré. Si vous le transmettez à une fonction, le nom arrayse décompose en un pointeur , et sizeof ne vous donnera pas la taille du tableau.

Faisons une expérience:

#include <stdio.h>

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écompose en un pointeur vers son premier élément lorsqu'il est passé à f(). Par conséquent, les tailles imprimées dans f() sont "incorrectes". En outre, 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 du 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 que vous avez postée ne sont pas équivalentes au sens strict. 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 ayez besoin de vous en préoccuper.

73
Alok Singhal

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

Cela semble étrange, 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, cela efface toujours le tableau, mais seulement quand quelque chose est écrit dans le tableau. Ce n'est pas un gros avantage ici. Toutefois, si le tableau 2D a été implémenté en utilisant, par exemple, un arbre quadrilatère (et non un esprit dynamique), ou un ensemble de lignes de données, vous pouvez localiser l’effet du drapeau booléen, mais vous aurez besoin de plus de drapeaux. Dans l’arbre à quatre, définissez simplement le drapeau vide pour le nœud racine, dans le tableau de lignes, définissez le drapeau pour chaque ligne.

Ce qui conduit à la question "pourquoi voulez-vous mettre à zéro à plusieurs reprises un grand tableau 2D"? A quoi sert le tableau? Existe-t-il un moyen de changer le code de sorte que le tableau ne nécessite pas de réduction à zéro?

Par exemple, si vous aviez:

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

en d’autres termes, utilisez-le comme tampon d’accumulation, puis changez-le comme ceci améliorerait les performances 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 d'effacement du tableau, mais fonctionne toujours. Et ce sera beaucoup plus rapide que d'effacer le tableau. Comme je l'ai dit, le moyen le plus rapide est de ne pas le faire en premier lieu.

9
Skizz

Si vous êtes vraiment, vraiment obsédé par la vitesse (et non par la portabilité), je pense que le moyen absolu le plus rapide de le faire serait d'utiliser le vecteur SIMD. les intrinsèques. par exemple. sur les processeurs Intel, vous pouvez utiliser les instructions SSE2 suivantes:

__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 entiers de 32 bits à zéro en un coup.

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

Faites-le en boucle et déroulez-le plusieurs fois pour obtenir un initialiseur rapide:

// 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] __attribute__ ((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 y a aussi une variante de _mm_storeu qui contourne le cache (c’est-à-dire que la remise à zéro du tableau ne pollue pas le cache), ce qui peut vous apporter des avantages en termes de performances secondaires dans certaines circonstances.

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

8
Jarrod Smith

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

5
Ben Zotto

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

... au moins dans GCC 4.8.

3
Engineer

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);
2
Pablo Santa Cruz

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

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

// toutes les cellules de a ont été initialisées à 0

1
DUDE_MXP

Je pense que le moyen le plus rapide de le faire à la main est de suivre le code. Vous pouvez comparer sa vitesse à la fonction memset, mais cela ne devrait pas être plus lent.

(changez le type de pointeurs ptr et ptr1 si votre type de tableau est différent, alors int)


#define SIZE_X 100
#define SIZE_Y 100

int *ptr, *ptr1;
ptr = &array[0][0];
ptr1 = ptr + SIZE_X*SIZE_Y*sizeof(array[0][0]);

while(ptr < ptr1)
{
    *ptr++ = 0;
}
0
mack369
memset(array, 0, sizeof(int [n][n]));
0
swestrup

Tu peux essayer ça

int array[20,30] = {{0}};
0
Călin Calin