web-dev-qa-db-fra.com

effacement d'un petit tableau entier: memset vs for loop

Il existe deux façons de mettre à zéro un tableau entier/flottant:

memset(array, 0, sizeof(int)*arraysize);

ou:

for (int i=0; i <arraysize; ++i)
    array[i]=0;

évidemment, memset est plus rapide pour les gros arraysize. Cependant, à quel point la surcharge du memset est-elle réellement plus grande que la surcharge de la boucle for? Par exemple, pour un tableau de taille 5 - lequel serait le mieux? La première, la 2e, ou peut-être même la version non roulée:

array[0] = 0;
array[1] = 0;
array[2] = 0;
array[3] = 0;
array[4] = 0;
56
Claudiu

Selon toute vraisemblance, memset () sera inséré par votre compilateur (la plupart des compilateurs le traitent comme un `` intrinsèque '', ce qui signifie essentiellement qu'il est inséré, sauf peut-être aux optimisations les plus faibles ou à moins qu'il ne soit explicitement désactivé).

Par exemple, voici quelques notes de version de GCC 4. :

La génération de code du déplacement de bloc (memcpy) et de l'ensemble de blocs (memset) a été réécrite. GCC peut désormais choisir le meilleur algorithme (boucle, boucle non déroulée, instruction avec préfixe de répétition ou appel de bibliothèque) en fonction de la taille du bloc copié et de l'optimisation du processeur. Une nouvelle option -minline-stringops-dynamically a été ajouté. Avec cette option, les opérations de chaîne de taille inconnue sont développées de telle sorte que les petits blocs sont copiés par du code en ligne, tandis que pour les grands blocs, un appel de bibliothèque est utilisé. Il en résulte un code plus rapide que -minline-all-stringops lorsque l'implémentation de la bibliothèque est capable d'utiliser des indications de hiérarchie de cache. L'heuristique de choix de l'algorithme particulier peut être écrasée via -mstringop-strategy. Récemment aussi, memset de valeurs différentes de 0 est inséré.

Il pourrait être possible pour le compilateur de faire quelque chose de similaire avec les exemples alternatifs que vous avez donnés, mais je parie que c'est moins probable.

Et il est grep- capable et plus immédiatement évident en un coup d'œil quelle est l'intention de démarrer (pas que la boucle soit particulièrement difficile à bloquer non plus).

45
Michael Burr

Comme Michael l'a déjà noté, gcc et je suppose que la plupart des autres compilateurs optimisent déjà très bien cela. Par exemple, gcc transforme ceci

char arr[5];
memset(arr, 0, sizeof arr);

dans

movl  $0x0, <arr+0x0>
movb  $0x0, <arr+0x4>

Ça ne va pas mieux que ça ...

21
bdijkstra

Il n'y a aucun moyen de répondre à la question sans mesurer. Cela dépendra entièrement des implémentations du compilateur, du processeur et de la bibliothèque d'exécution.

memset () peut être un peu une "odeur de code", car il peut être sujet à des débordements de tampon, à des inversions de paramètres et a la malheureuse capacité de n'effacer que les "octets". Cependant, il y a fort à parier qu'il sera "le plus rapide" dans tous les cas sauf les cas extrêmes.

J'ai tendance à utiliser une macro pour envelopper cela afin d'éviter certains des problèmes:

#define CLEAR(s) memset(&(s), 0, sizeof(s))

Cela évite les calculs de taille et élimine le problème de permutation des paramètres de longueur et de valeur.

En bref, utilisez memset () "sous le capot". Écrivez ce que vous avez l'intention et laissez le compilateur s'inquiéter des optimisations. La plupart y sont incroyablement bons.

8
Roddy

Compte tenu de ce code en soi, tout a déjà été dit. Mais si vous le considérez dans son programme, dont je ne sais rien, quelque chose d'autre peut être fait. Par exemple, si ce code doit être exécuté à chaque fois pour effacer un tableau, vous pouvez exécuter un thread qui alloue en permanence un nouveau tableau de zéro éléments affectés à une variable globale, que votre code, lorsque le tableau doit être effacé, pointe simplement vers.

Ceci est une troisième option. Bien sûr, si vous prévoyez d'exécuter votre code sur un processeur avec au moins deux cœurs et cela a du sens. Le code doit également être exécuté plusieurs fois pour voir les avantages. Pour une seule exécution ponctuelle, vous pouvez déclarer un tableau rempli de zéros, puis le pointer vers vous en cas de besoin.

J'espère que cela peut aider quelqu'un

1
fedeb