Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

memset ou boucle for

6 réponses
Avatar
Lucas Levrel
Bonjour,

Je dois initialiser à zéro un tableau de doubles, dans un programme
compilé avec le compilateur Intel en -O3 et exécuté sur un Xeon.

Qu'est-ce qui sera le plus rapide à l'exécution : memset, une boucle for,
ou ex-æquo ?

Merci pour vos lumières.

--
LL

6 réponses

Avatar
Marc Boyer
Le 06-02-2012, Lucas Levrel a écrit :
Je dois initialiser à zéro un tableau de doubles, dans un programme
compilé avec le compilateur Intel en -O3 et exécuté sur un Xeon.

Qu'est-ce qui sera le plus rapide à l'exécution : memset, une boucle for,
ou ex-æquo ?



Disons que ta question n'a pas beaucoup de sens.

Déjà, j'imagine que tu vas faire un memset en mettant tous les
bits à 0, en faisant l'hypothèse qu'un float dont tous les bits
sont nuls vaut 0.0.
C'est sûrement le cas sur ton Xeon, mais je ne pense pas
que ce soit garanti par la norme. L'avantage de la boucle
for est d'ếtre portable.

Côté lisibilité, je miserais aussi sur la boucle for.

Côté perf pures, j'ai du mal à imaginer un contexte
où la mise à 0 d'un tableau de flottant soit une partie
critique en temps de calcul. Ca faut quand même phase
d'initialisation, pas le coeur du souci.

Tout ceci étant dit, ça peut dépendre de pleins de choses.

Supposons que le compilo n'ait pas de traitement spécifique
pour la fonction memset: on compare la qualité de l'implantation
de memset à la qualité de l'optimiseur d'icc. Donc, faudrait
préciser quel memset, de quelle libc.
Quand memset reçoit ton tableau, il ne sait pas s'il est
bien aligné ou pas, et il doit calculer s'il a le droit de faire
des grandes écritures de 64 bits, ou pleins de petites de 8 bits.
Alors que le compilo le sait lui.
Si memset est une fonction "comme une autre", il y a un appel
de fonction, peut-être un défaut de cache.

Mais il est aussi possible que le compilo fasse de l'appel à
memset un cas particulier (il "reconnait" cette fonction).

Tout ça pour dire que le plus simple, une fois que tu t'es
assuré que tu avais vraiment besoin de savoir ça, que la
performance primait sur la lisibilité, il te reste à faire les
tests, sur différentes tailles de tableau (les résultats
pourraient varier), éventuellement à regarder l'assembleur
généré.

Marc Boyer
--
À mesure que les inégalités regressent, les attentes se renforcent.
François Dubet
Avatar
Antoine Leca
Lucas Levrel écrivit :
Je dois initialiser à zéro un tableau de doubles, dans un programme
compilé avec le compilateur Intel en -O3 et exécuté sur un Xeon.

Qu'est-ce qui sera le plus rapide à l'exécution : memset, une boucle
for, ou ex-æquo ?



Réponse générique pour toute question de micro-optimisation : tu testes
les deux options, et tu choisis la meilleure. Et *surtout*, tu notes
dans un coin de refaire ce test régulièrement (car l'environnement
général, par exemple les algorithmes choisis, peut avoir une influence)


Maintenant, dans ce cas en particulier, si memset() donne un plus
mauvais résultat qu'une boucle for, je commencerais par me préoccuper
sérieusement de la qualité en matière d'optimisation de la bibliothèque
standard que tu utilises.


Antoine
Avatar
espie
In article ,
Lucas Levrel wrote:
Bonjour,

Je dois initialiser à zéro un tableau de doubles, dans un programme
compilé avec le compilateur Intel en -O3 et exécuté sur un Xeon.

Qu'est-ce qui sera le plus rapide à l'exécution : memset, une boucle for,
ou ex-æquo ?



Si c'est vaguement correctement foutu, c'est la bande passante d'acces
a la memoire qui va dominer, de tres tres loin. Dans les deux cas,
tu mets plein d'octets "a zero" (confere les remarques des autres sur
la portabilite de l'operation!) et c'est l'acces memoire qui va prendre
le plus de temps, loin devant les instructions processeur...

En fait, si tu veux gagner du temps, il faut compter les vrais temps d'acces
a la memoire et au cache: on n'a pas la moindre idee de ce que tu nous
ponds comme algo, mais ca peut etre interessant de mettre ton tableau "a 0"
juste avant d'en faire quelque chose, histoire que les donnees concernees
soient encore dans le cache.
Avatar
Xavier Roche
Le 06/02/2012 17:54, Antoine Leca a écrit :
Maintenant, dans ce cas en particulier, si memset() donne un plus
mauvais résultat qu'une boucle for, je commencerais par me préoccuper
sérieusement de la qualité en matière d'optimisation de la bibliothèque
standard que tu utilises.



Ça dépend: remplir un petit tableau avec une petite boucle sans
effectuer de branchement dans la bibliothèque standard (et donc rester
sur la même ligne de cache de code), pour au final déplacer le même
nombre d'octets de la mémoire vers le cache, le gagnant n'est pas
évident (j'imagine que le facteur limitant sera mémoire <=> cache ici).

Après, on peut espérer que memset() soit un "builtin" dans pas mal de cas.

Mais bon, micro-optimisation, en effet, et totalement anecdotique amha.

(pour la portabilité, le memset() est probablement zen, dans la mesure
où 0.0 == tous les bits a zéro en IEEE 754 -- mais il faut travailler
dans un environnement qui suit IEEE 754 évidemment :p)
Avatar
Erwan David
Xavier Roche écrivait :

Le 06/02/2012 17:54, Antoine Leca a écrit :
Maintenant, dans ce cas en particulier, si memset() donne un plus
mauvais résultat qu'une boucle for, je commencerais par me préoccuper
sérieusement de la qualité en matière d'optimisation de la bibliothèque
standard que tu utilises.



Ça dépend: remplir un petit tableau avec une petite boucle sans
effectuer de branchement dans la bibliothèque standard (et donc rester
sur la même ligne de cache de code), pour au final déplacer le même
nombre d'octets de la mémoire vers le cache, le gagnant n'est pas
évident (j'imagine que le facteur limitant sera mémoire <=> cache ici).

Après, on peut espérer que memset() soit un "builtin" dans pas mal de cas.

Mais bon, micro-optimisation, en effet, et totalement anecdotique amha.

(pour la portabilité, le memset() est probablement zen, dans la mesure
où 0.0 == tous les bits a zéro en IEEE 754 -- mais il faut travailler
dans un environnement qui suit IEEE 754 évidemment :p)



S'il s'agit de mettre tous les bits à 0, initialiser en faisant
double tab[size]={0.0} marchera aussi.
Et c'est le compilateur qui choisira la manière de le faire

*attention* ne fonctionne que si 0.0 est représenté avec tous les bits à
0, mais comme on parle de le faire avec memset, on reste avec les même
contraintes sur la plateforme.


--
Le travail n'est pas une bonne chose. Si ça l'était,
les riches l'auraient accaparé
Avatar
Lucas Levrel
Merci pour toutes les remarques fort instructives, je n'en attendais pas
moins des contributeurs du groupe !

Le coût de l'initialisation en question sera en effet mineur. C'est une
remarque dans un bouquin, qui indiquait que memset serait plus rapide, qui
a éveillé ma curiosité.


--
LL