web-dev-qa-db-fra.com

memset () est-il plus efficace que pour la boucle en C?

est memset plus efficace que pour la boucle. donc si j'ai

char x[500];
memset(x,0,sizeof(x));

ou

char x[500];
for(int i = 0 ; i < 500 ; i ++) x[i] = 0;

lequel est le plus efficace et pourquoi? existe-t-il une instruction spéciale dans le matériel pour effectuer l'initialisation au niveau du bloc.

36
David

Très certainement, memset sera beaucoup plus rapide que cette boucle. Notez comment vous traitez un caractère à la fois, mais ces fonctions sont tellement optimisées qu'elles définissent plusieurs octets à la fois, même en utilisant, lorsqu'elles sont disponibles, MMX et SSE instructions.

Je pense que l'exemple paradigmatique de ces optimisations, qui passent généralement inaperçues, est la fonction GNU C library strlen. On pourrait penser qu'elle a au moins O(n) performances, mais il a en fait O(n/4) ou O(n/8) selon l'architecture (oui , Je sais, en gros O() sera le même, mais vous obtenez en fait un huitième du temps Comment? Tricky, mais joliment: strlen .

29
Diego Sevilla

Eh bien, pourquoi ne pas jeter un coup d'œil au code d'assemblage généré, optimisation complète sous VS 2010.

char x[500];
char y[500];
int i;      

memset(x, 0, sizeof(x) );   
  003A1014  Push        1F4h  
  003A1019  lea         eax,[ebp-1F8h]  
  003A101F  Push        0  
  003A1021  Push        eax  
  003A1022  call        memset (3A1844h)  

Et votre boucle ...

char x[500];
char y[500];
int i;    

for( i = 0; i < 500; ++i )
{
    x[i] = 0;

      00E81014  Push        1F4h  
      00E81019  lea         eax,[ebp-1F8h]  
      00E8101F  Push        0  
      00E81021  Push        eax  
      00E81022  call        memset (0E81844h)  

      /* note that this is *replacing* the loop, 
         not being called once for each iteration. */
}

Donc, sous ce compilateur, le code généré est exactement le même. memset est rapide, et le compilateur est suffisamment intelligent pour savoir que vous faites la même chose que d'appeler memset une fois de toute façon, donc il le fait pour vous.

Si le compilateur a effectivement laissé la boucle telle quelle, cela serait probablement plus lent car vous pouvez définir plus d'un bloc de taille d'octet à la fois (c'est-à-dire que vous pouvez dérouler votre boucle un peu au minimum. Vous pouvez supposer que memset sera au moins aussi rapide qu'une implémentation naïve telle que la boucle. Essayez-la sous une version de débogage et vous remarquerez que la boucle n'est pas remplacée.

Cela dit, cela dépend de ce que le compilateur fait pour vous. Regarder le démontage est toujours un bon moyen de savoir exactement ce qui se passe.

33
Ed S.

Cela dépend vraiment du compilateur et de la bibliothèque. Pour les compilateurs plus anciens ou les compilateurs simples, memset peut être implémenté dans une bibliothèque et ne fonctionnerait pas mieux qu'une boucle personnalisée.

Pour presque tous les compilateurs qui valent la peine d'être utilisés, memset est une fonction intrinsèque et le compilateur va générer du code en ligne optimisé pour cela.

D'autres ont suggéré de profiler et de comparer, mais je ne me dérangerais pas. Utilisez simplement memset. Le code est simple et facile à comprendre. Ne vous inquiétez pas jusqu'à ce que vos tests de référence vous indiquent que cette partie du code est un point chaud de performance.

12
Michael

La réponse est "cela dépend". memset PEUT être plus efficace, ou il peut utiliser en interne une boucle for. Je ne peux pas penser à un cas où memset sera moins efficace. Dans ce cas, cela peut devenir une boucle for plus efficace: votre boucle itère 500 fois en définissant une valeur de 0 octets du tableau à chaque fois. Sur une machine 64 bits, vous pouvez effectuer une boucle, en définissant 8 octets (un long long) à la fois, ce qui serait presque 8 fois plus rapide, et en traitant simplement les 4 octets restants (500% 8) à la fin.

ÉDITER:

en fait, c'est ce que memset fait dans la glibc:

http://repo.or.cz/w/glibc.git/blob/HEAD:/string/memset.c

Comme Michael l'a souligné, dans certains cas (où la longueur du tableau est connue au moment de la compilation), le compilateur C peut inline memset, en supprimant la surcharge de l'appel de fonction. Glibc dispose également de versions optimisées pour l'assemblage de memset pour la plupart des plates-formes majeures, comme AMD64:

http://repo.or.cz/w/glibc.git/blob/HEAD:/sysdeps/x86_64/memset.S

8
Bobby Powers

Les bons compilateurs reconnaîtront la boucle for et la remplaceront par une séquence en ligne optimale ou un appel à memset. Ils remplaceront également le memset par une séquence en ligne optimale lorsque la taille du tampon est petite.

En pratique, avec un compilateur d'optimisation, le code généré (et donc les performances) sera identique.

3
Stephen Canon

D'accord avec ci-dessus. Ça dépend. Mais, à coup sûr, le memset est plus rapide ou égal à la boucle for. Si vous n'êtes pas sûr de votre environnement ou que vous êtes trop paresseux pour tester, prenez la route sûre et optez pour le memset.

2
beetree