memset ou boucle for

Le
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
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Marc Boyer
Le #24224721
Le 06-02-2012, Lucas Levrel
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
Antoine Leca
Le #24224631
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
espie
Le #24224701
In article 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 ?



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.
Xavier Roche
Le #24225161
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)
Erwan David
Le #24225221
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)



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é
Lucas Levrel
Le #24225301
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
Publicité
Poster une réponse
Anonyme