SSE, Pentium, Celeron, Athlon et autres sont sur un bateaux

Bon, une fois n’est pas coutume, j’ai un petit soucis avec la mise en place du SSE dans un projet perso (un raytracer temps réel).

Je vous explique mon problème : j’ai des classes mathématiques de base (CVector, CPoint, CHPoint, CMatrix3x3, CMatrix4x4, etc.) Ces classes sont à la base implémentées en C++ tout bête, donc utilisent la FPU classique & tout & tout. Jusque la, tout va bien.

Maintenant, l’idée c’est d’exploiter le SSE qu’il est bien (et rapide, en théorie) pour accélérer tous ces calculs. Donc j’ai écris une version de la plupart de ces classes (pas toutes encore, ça prends du temps quand même) en SSE, via les intrinsics de Visual Studio .NET (rulez). Du coup, la plupart des opérations se font en SSE (addition/soustraction/multiplication/dot products, etc.), ce qui est bien.

Le problème, c’est que quand je compile le raytracer avec ces classes (avec tout l’alignement qui va bien, puisqu’il faut aligner sur 16 octets les éléments accédés par le SSE), celui-ci est plus lent qu’en FPU. Oui, plus lent. Sur mon Athlon 1600+ XP, je passe de 50 FPS à 45 FPS pour une scène donnée. Et la même scène, sur un Celeron 2Ghz, passe de 30 FPS à 10 FPS. Quand même

Je ne comprends pas d’où peut venir le problème. Ok je n’ai pas toutes les opérations en SSE encore, mais une grosse partie quand même.

Si l’un d’entre vous sait d’où peut venir le problème, ça m’intéresse. D’autant que je me suis basé sur le principe de la “Math Lib 2” de Intel, qui fait pareil que moi : faire des classes mathématiques en SSE. Et eux disent que ça accélérent.

(Petite note : sur mes tests “hors contexte” des opérateurs de mes classes, le SSE semble plus rapide d’un facteur 1.5 à 3 suivant les opérateurs et les cas, alors que quand je le met en condition, il apparait comme étant plus lent.)

(Note 2 : c0unt0, pas la peine de te foutre de ma gueule hein… )

hahahaha

Bon plus serieusement : t’es intrinsiques et ton compilo, tu es sur qu’ils enchainent bien ? et qu’il te font pas un truc du genre 

lit data
SSE data (1 operation)
ecrit data
lit data
SSE data (1 operation)
ecrit data
lit data
SSE data (1 operation)
ecrit data

plutot que

lit data
SSE data (1 operation)
SSE data (1 operation)
SSE data (1 operation)
ecrit data

parce qu’un certain CW le fait tres tres bien (le lourd)…
sinon je dirais : copy constructor (s’t’une pute ce truc la : si ca se trouve tu aloue plein de truc sur la pile pendant tes operations…)
ou encore verifie que VC6/7/Net/Whatever ne te rajoute pas de la routine de verif (overflow et compagnie) entre chaque operation (un peu comme le check pour les flotant si tu ne compiles pas en mode "super-aggressif-si-tu-me-manges-un-cycle-je-te-pete-la-gueulle) ?

haha

[Enleve les remarques sur l’alignement : tu le disais dans le msg]

Ce message a été édité par c0unt0 le 22/10/2003

Ca se fait pas de se moquer des gens gentils comme moi Le code lui même semble plutôt propre sur lui (où qu’il est tout joli), plutôt pas mal optimisé (sauf certains points mais bon, aucun compilo n’est parfait).

Pour le copy constructor, ben il est en SSE et j’ai tellement d’optims de partout que ça se résume à une copie d’un registre SSE à un autre Vérifié / re-checké / testé / validé / gravé dans du marbre. 

VC7 (puisque c’est lui que j’utilise) ne rajoute pas de check (j’ai mis les bonnes options d’optim de partout pour tout).

En fait j’ai amélioré le code. Maintenant la version SSE va aussi vite (enfin, un tout petit peu plus vite en fait) que la version FPU. Youpi. Pour ça, j’ai dû réduire drastiquement la quantité de code restant effectué par le FPU. Apparemment, le SSE aime pas le mélange SSE/FPU, alors que les docs disent que c’est possible & tout & c’est génial & … bref. Le céléron est passé de 10 à 30 FPS, comparable à la version FPU. Et sur mon 1600+ XP je suis à 51/52 en SSE contre 50/51 en FPU. Pas encore top.

Bref, je désespére pas trouver le truc qui doit encore me ralentir (parce que merde, le SSE ça doit bourrer normalement !) En attendant, ça me fait gagner de la taille de code (oui, ça compte, c’est pour une 64ko )

que le passage SSE/FPU te prennes de la resource : ca ne m’etonne pas : sachant que tu es sur AMD, ca ne m’etonerais pas que tu te tapes de l’aliasing/desaliasing de registre ou des merde comme ca, a essayer sur un pountioum !
De toute facons, en regle general, a moins de pouvoir faire du parallele (VU0/CPU-FPU quelqu’un ?) il est plutot deconseiller de mixer, je sais que du temps ou je faisais de l’x86, j’essayais toujours de pas melanger cpu/fpu ou cpu/mmx histoire d’etre sur que j’etait pepere sur le plus long segment de code possible

sinon pour les copy constructor : y a pas moyens de les zapper ? mais vraiment je veux dire ? (C++ de hsjiouore54%$£"%$ (pas de troll merci )

Et Je me moque pas, je rie, c’est tout

Au contraire, c’est mieux chez AMD que chez Intel justement Terribeul non ?

Pour ce qui est du mixing, en théorie le CPU se mixe bien avec tout (unités de calculs vraiment séparée) alors que le MMX/FPU/SSE a plutôt tendance à être un conglomérat de bizarreries internes Donc ça m’étonne pas que ce soit moins efficace de mixer FPU/SSE que de faire du SSE pur, par contre ça m’étonne que le SSE/FPU soit plus lent que le FPU pur

Bref, je continue à bosser dessus.

(Pour le copy constructor : le compilo il est malin, il optimise bien ce truc la, qui n’est d’ailleurs pas si souvent utilisé que ça en fait si tu regardes bien… moi il me coute 0 la )

Putain et moi qui croyait savoir programmer… la je suis completement largué.

Bon j’y connais pas grand chose en programmation a bas niveau de ce genre la mais c’est toujours rigolo de regarder Alors pas de foutage de gueule si je dis une betise…

Moi j’ai toujours lu que melanger MMX/FPU c’etait MAL parceque le switch etait super lent et qu’il avait a faire un reset des flags sur les registres a chaque switch. Mais bon avec SSE ca doit pas etre un probleme puisqu’il a ses propres registres et qu’il y a pas de changement de mode. (oui je me doute que vous le savez par coeur ca, c’est pour moi )

Apparement l’alignement de tes data quand tu fais un read est super important mais t’as deja fait ca… il reste que l’aliasing ou l’ordre des load apparement…

J’ai trouve ca:

http://www.gamedev.net/reference/articles/article1097.asp
http://www.codeplay.com/manual/alignment.html
http://www.codeplay.com/manual/optimization.html <— a l’air tres bien

L’aide minable d’un dev haut niveau pour de l’assembleur est la

Eheh, vive le bas niveau

Sinon, pour répondre à GloP, ben MMX/FPU c’est problématique quand c’est mélangé à cause du fait que leurs registres sont partagés : c’est le même espace mémoire qui est alloué pour les 2 jeux de registre. Donc a un instant T tu ne peux utiliser cet espace mémoire que en tant que FPU ou MMX. Le switch de FPU->MMX est “gratuit”, il n’y a rien a faire, par contre l’inverse n’est pas vrai. Switcher du MMX vers le FPU necessite l’utilisation de l’instruction EMMS, qui coute une douzaine de cycles quand même.

L’interet du SSE c’est d’apporter son propre jeu de registres justement. Donc, a priori, il n’y a plus de problème de contexte. Cela dit, il semblerait qu’en fait la plupart des instructions SSE ait un throughput de 2 cycles en général, et une latency de 4 cycles. Et qu’en plus, certaines instructions (comme MULPS pour multiplier 4 float par 4 float en une instruction) utilisent en fait la même execution unit que la FPU. Donc, ça ferait staller la FPU en même temps que le SSE. Donc ça expliquerait que ça soit pas optimal le mélange des deux.

(vive les docs Intel cryptiques pour trouver ces infos…)

Bref, voila les joies en direct du front, j’y retourne

Ok j’avais bien pige pourquoi c’etait lent MMX/FPU et pourquoi SSE/FPU avait pas ce probleme mais apparement il partage quand meme des trucs on dirait. Les docs a ce niveau sont graves imbitables je me suis toujours dit que c’etait parceque j’etais pas dedans mais apparement meme si t’es dedans c’est cryptique quand meme hehe. Au secour.

Tiens nous au courant. T’as matte les liens sinon? Des trucs interessant que tu savais pas ou c’etait le B-A-BA?

Ben disons que je me remet à l’assembleur PC après 2 ans de PS2 / GameCube donc bon… pis de toute façon les docs constructeur, c’est toujours cryptique, c’est pas fait pour être clair

Accessoirement, ça me pose des problèmes de précision (avec les divisions ayant un taux d’erreur maximal de type |Erreur| <= 1.5 x 2^-12, c’est pas terribeul). Donc si quelqu’un a des tips sur la manière d’augmenter la précision ça m’intéresse.

D’ailleurs si tu es blonde à forte poitrine et que tu n’as pas peur d’un geek codeur, tu m’interesse aussi Mais c’est off-topic ça
Ce message a été édité par tuo le 24/10/2003

As-tu essayé de mesurer la répartition du temps CPU passé dans ton “appli” avant d’optimiser “à l’aveuglette” ? et en utilisant les options d’optimisations du compilo ? typiquement -O3 pour voir les différences avec l’usage des SSE ?

Les instructions SSE/SSE2 ne s’utilisent pas “comme ça”. D’une part il y a une question d’alignement de données sur 128 bits qui est très importante, d’autre part le but du SSE/SSE2 est de faire du SIMD, donc si ton code n’est pas adapté pour cela, il n’ira pas plus vite.

Je te conseille d’utiliser un outil comme VTune pour bien regarder où ton code merdouille pas impossible que tu sous-utilises le pipeline.

(Autre détail, ton AMD (de merde, troll ;)), n’est pas nécessairement un bon Oracle pour tester les performances de ton code SSE.).

ils sont mignons les deux dernier… en meme temps ils savent pas, alors je vais leurs dire :
Si je n’etait pas aussi megalo (et aussi bon) et qu’il etait un poil meilleurs (et moi, moins bon), je pourais presque considerer Tuo comme un “l33t c0d3r”

Donc je pense que les options de son compilos sont bonnes, que son algo est fait pour faire du parallele (ou alors il a beaucoup oublie depuis son passage sur ps2 ), que ses donnees sont alignees (sur 16 bytes, et non pas 128… parce que bon faut pas pousser non plus : c’est pas du VU, hein, le SSE)… et que si il est pas en train d’optimiser son inner-loop (la partie qui doit VRAIMENT allez vite dans son algo) : il va se faire boter le cul dans pas longtemps

Sinon pour la precision, ca ne m’etonne pas des masses : c’est le cas de la plus part des FPU bourines (VU/FPU ps2, SSE, single paired GCN et autres) : il vaut mieux eviter les algos entre -1.0 et +1.0, ainsi que ceux qui se reposent sur le signe de zero (car oui, des fois, zero a un signe.) !

Et puis une autre question, ta boucle : elle tiens dans le cache ? tu te fairais pas pourrir tes acces memoires donne et/ou cpu ?

héhé C0unt0, ne te fâche pas, je pense qu’il est toujours bon de poser des questions “primaires”. J’ai souvent vu des développeurs avec des à priori ENORMES (heuu pas uniquement des dévelopeurs cela dit mais c’est un autre sujet) faire de l’optimisation de blaireau. De plus, il n’est pas toujours évident de savoir exactement où (DTC proof) le CPU est utilisé. Pour ce qui concerne tuo, ma remarque est peut-être décallée mais nous n’avons pas beaucoup d’infos, non plus .

Heuu je n’ai pas compris l’allusion 16 bytes / 128 bits / VU  pourrais-tu préciser ?

Merci c0unt0 de venir à mon secours
Ce message a été édité par tuo le 27/10/2003