[C/C++] Optimisations diverses : besoin d'astuces

Optimisations diverses : besoin d'astuces [C/C++] - C - Programmation

Marsh Posté le 29-11-2007 à 12:45:58    

Salut à tous !
 
j'ai une question toute bête mais bon
 
j'ai des variables cR, cG et cB de type int que je veux conserver entre 0 et 255 (pour choper une correspondance de couleur dans une LUT).
Pour n'avoir pas à tester si elles sont inférieurs à 0, j'ai passé le type en unsigned int, c déjà ca de gagner comme cycle machine.
Mais il ya t'il un truc, une astuce (masque binaire ?) pour éviter d'avoir à faire des

Code :
  1. if (cR > 255) cR = 255;


 
Je sais pas c pas super clair, mais bon si vous avez des astuces d'optimisations diverses dans le genre, je suis preneur :D


---------------
We deserve everything that's coming...
Reply

Marsh Posté le 29-11-2007 à 12:45:58   

Reply

Marsh Posté le 29-11-2007 à 12:51:11    

cr &= 255;

Reply

Marsh Posté le 29-11-2007 à 12:57:27    

oui mais non :D
 
paske si cR = 256 soit 0x100, ca donne une fois le masque appliqué 0x00, or moi je voudrais que "cale" sur 0xFF (j'ai pas été super là dessus en fait :jap: )
 
c le même principe que cR %= 256 en fait


---------------
We deserve everything that's coming...
Reply

Marsh Posté le 29-11-2007 à 13:00:22    

Bien sûr. Désolé je me suis trompé. Excusez-moi. Il faut un ou binaire au lieu du et binaire, donc
cR |= 255.
 
EDIT : NazzTazz a raison le ou binaire ne va pas non plus. Je n'avais pas compris que pour 256, 257, ... l'infini, il fallait avoir 255.


Message édité par olivthill le 29-11-2007 à 13:21:43
Reply

Marsh Posté le 29-11-2007 à 13:17:29    

 

Pour le OU logique oui en effet, les OU "forcent" les bits, mais merci quand meme :)

 

Pour ton expression NazzTazz, ca reste un test conditionnel, mais je vais voir quand même comment réagi le compilateur (dans mon j'utilise dev-C++)

 

EDIT:
testé... et ca revient au même sorry :jap:

 

Pour la petite histoire, je me suis mis en tête de faire une petite routine de bumpmapping 2D avec SDL (demomaking toussa :D), et j'affiche le nombre de FPS en temps réel. Si un ou deux FPS en + ou - ne changent rien, si j'en perds ou gagne 10 par ci par là, c bien ;)
Pour info, ma routine tournait à 140-150 FPS sur ma machine du taf (640x480x32 software), et en optimisant des trucs un peu partout, ca devient super spécifique, mais ca tourne maintenant à 210 environ. Et je veux voir jusqu'où on peut aller juste en "grattant" un peu partout ;)

 

affichage :
http://www.oldskoul.com/upload/FastUpload/60fd3848992cdd2c73664e26ef93f4f7.jpg


Message édité par SICKofitALL le 29-11-2007 à 13:28:16

---------------
We deserve everything that's coming...
Reply

Marsh Posté le 29-11-2007 à 13:26:17    

utilise des char ?

Reply

Marsh Posté le 29-11-2007 à 13:45:00    

:spamafote:
ben oui mais ca "reboucle", si la val passe à 0x100, on se retrouve de nouveau avec 0x00, et moi je voudrais limiter la val à 0xFF au max.
 
Je me demande si c'est possible en fait de faire en se passant d'un test conditionnel ou d'une re-analyse générale :/


---------------
We deserve everything that's coming...
Reply

Marsh Posté le 29-11-2007 à 13:54:02    

SICKofitALL a écrit :

:spamafote:
ben oui mais ca "reboucle", si la val passe à 0x100, on se retrouve de nouveau avec 0x00, et moi je voudrais limiter la val à 0xFF au max.
 
Je me demande si c'est possible en fait de faire en se passant d'un test conditionnel ou d'une re-analyse générale :/

la val passe jamais à 0x100. un char quoi

Reply

Marsh Posté le 29-11-2007 à 14:01:39    

je suis d'accord, mais en interne il fera un cast et prendra les bits de point faible pour obtenir son char ;)


---------------
We deserve everything that's coming...
Reply

Marsh Posté le 29-11-2007 à 14:20:05    

le coup est lié au branchement potentiel.
regarde le code asm généré, peut-être que le compilo a utilisé un cmov, ce qui fait que l'expression initiale que tu as est efficace.
 
de toutes manières il faut connaitre l'archi que tu cibles, et ce que le compilo génére.


Message édité par bjone le 29-11-2007 à 14:27:34
Reply

Marsh Posté le 29-11-2007 à 14:20:05   

Reply

Marsh Posté le 29-11-2007 à 14:29:43    

SICKofitALL a écrit :

je suis d'accord, mais en interne il fera un cast et prendra les bits de point faible pour obtenir son char ;)


nan hein. J'ai un peu de mal à avoir en quoi quelques mov*b* serait plus lent que toutes tes branches ?

Reply

Marsh Posté le 29-11-2007 à 14:36:14    

ok ok :)
jvais zieuter le code généré de plus près :jap:
 
Ceci dit, le compilo qui va avec dev-C++ (gcc.exe/g++.exe) ne me semble pas être superbissime, ou alors je m'y prends mal je sais pas, jvais creuser...


---------------
We deserve everything that's coming...
Reply

Marsh Posté le 29-11-2007 à 14:38:32    

on va dire que ton compilateur, si t'as des registres 32bits, il va travailler avec ses registres sans limites. mais chaque fois que nécessaire, il fera la troncature. Genre si tu fais une somme de char, t'auras un registre général de la partie, il n'y aura aucune différence avec une somme d'int, sauf l'instruction finale qui fera le casage correct.

Reply

Marsh Posté le 29-11-2007 à 14:50:21    

Taz a écrit :


nan hein. J'ai un peu de mal à avoir en quoi quelques mov*b* serait plus lent que toutes tes branches ?


 
les mov ne clampent pas  [:spamafote]

Reply

Marsh Posté le 29-11-2007 à 15:21:05    

>Taz
 
Oui mais là je baigne dans le flou :D
Ca voudrait dire que si je fais :

Code :
  1. unsigned char v1 = 255;
  2. unsigned char v2 = 2;
  3. unsigned char r = 0;
  4. r = v1 + v2;


donc r va être égal à 255 ou alors au "reste", càd 1 ? :??:


---------------
We deserve everything that's coming...
Reply

Marsh Posté le 29-11-2007 à 15:23:15    

bjone a écrit :


 
les mov ne clampent pas  [:spamafote]


si tu copies que 8 bits, t'as pas l'impression que ça fait la même chose ?

Reply

Marsh Posté le 29-11-2007 à 22:04:56    

Si le temps est vraiment une question de vie ou de mort, si vous vous moquez de la portabilité comme de l'an quarante, si vous n'etes pas allérgique à l'assembleur et enfin si vous avez du temps à perdre, bah y'a un groupe d'instructions pour 86/amd récent (2003 au moins) qui fait exactement ce que vous voulez en SIMD et tout et tout. Ca s'appelle la saturation. Y'a 3 types d'opérations différentes et elles s'effectuent soit sur les régistres MMX, soit sur les SSE (ces derniers étant toujours plus pratiques à utiliser):
 
packsswb/dw : juste une simple saturation 8 mots -> 8 octets en même temps (wb), ou 4 double -> 4 mots (dw). Le tout en 2 cyles processeurs.
packuswb : idem en unsigned.
 
Mais là ou c'est intéressant, c'est une addition en saturation:
 
paddsb/paddsw : addition + saturation, 8 x 16bits -> 8 x 8bits (wb) ou  4 x 32bits -> 4 x 16 bits  (dw). Pas plus couteuse, en 2 cycles aussi.
paddusb/paddusw: idem en unsigned
 
psubsb/psubsw : soustraction + saturation
psubusb/psubusw: idem en unsigned
 
Je ne crois pas que l'équivalent avec la mutiplication existe, de toute façon, ça serait inutile. Sinon, en restant en C pur, le plus performant restera de toute façon l'opérateur ternaire (en tous cas pour nos supers processeurs spéculatifs modernes).
 
cr = (cr > 255)? 255 : cr;
 
Par contre, je sais pas si j'ai bien compris, mais le fait passer votre cr en unsigned ne permettra pas de récupérer 0 à la place d'un nombre négatif, hein ?

Reply

Marsh Posté le 29-11-2007 à 22:09:44    

dave_tetehi a écrit :

Si le temps est vraiment une question de vie ou de mort, si vous vous moquez de la portabilité comme de l'an quarante, si vous n'etes pas allérgique à l'assembleur et enfin si vous avez du temps à perdre, bah y'a un groupe d'instructions pour 86/amd récent (2003 au moins) qui fait exactement ce que vous voulez en SIMD et tout et tout. Ca s'appelle la saturation. Y'a 3 types d'opérations différentes et elles s'effectuent soit sur les régistres MMX, soit sur les SSE (ces derniers étant toujours plus pratiques à utiliser):
 
packsswb/dw : juste une simple saturation 8 mots -> 8 octets en même temps (wb), ou 4 double -> 4 mots (dw). Le tout en 2 cyles processeurs.
packuswb : idem en unsigned.
 
Mais là ou c'est intéressant, c'est une addition en saturation:
 
paddsb/paddsw : addition + saturation, 8 x 16bits -> 8 x 8bits (wb) ou  4 x 32bits -> 4 x 16 bits  (dw). Pas plus couteuse, en 2 cycles aussi.
paddusb/paddusw: idem en unsigned
 
psubsb/psubsw : soustraction + saturation
psubusb/psubusw: idem en unsigned
 
Je ne crois pas que l'équivalent avec la mutiplication existe, de toute façon, ça serait inutile. Sinon, en restant en C pur, le plus performant restera de toute façon l'opérateur ternaire (en tous cas pour nos supers processeurs spéculatifs modernes).
 
cr = (cr > 255)? 255 : cr;
 
Par contre, je sais pas si j'ai bien compris, mais le fait passer votre cr en unsigned ne permettra pas de récupérer 0 à la place d'un nombre négatif, hein ?


 
merci pour ces infos :jap:
jme disais bien que ce genre de manips devaient d'une facon ou d'une autre être traité en interne genre TRES interne :D
 
sinon oui pour le unsigned on est bien daccord :)


---------------
We deserve everything that's coming...
Reply

Marsh Posté le 30-11-2007 à 00:47:50    

Taz a écrit :


si tu copies que 8 bits, t'as pas l'impression que ça fait la même chose ?


 
bin non, c'est pas un clamp.

Reply

Marsh Posté le 30-11-2007 à 00:53:43    

dave_tetehi a écrit :

Si le temps est vraiment une question de vie ou de mort, si vous vous moquez de la portabilité comme de l'an quarante, si vous n'etes pas allérgique à l'assembleur et enfin si vous avez du temps à perdre, bah y'a un groupe d'instructions pour 86/amd récent (2003 au moins) qui fait exactement ce que vous voulez en SIMD et tout et tout. Ca s'appelle la saturation. Y'a 3 types d'opérations différentes et elles s'effectuent soit sur les régistres MMX, soit sur les SSE (ces derniers étant toujours plus pratiques à utiliser):
 
packsswb/dw : juste une simple saturation 8 mots -> 8 octets en même temps (wb), ou 4 double -> 4 mots (dw). Le tout en 2 cyles processeurs.
packuswb : idem en unsigned.
 
Mais là ou c'est intéressant, c'est une addition en saturation:
 
paddsb/paddsw : addition + saturation, 8 x 16bits -> 8 x 8bits (wb) ou  4 x 32bits -> 4 x 16 bits  (dw). Pas plus couteuse, en 2 cycles aussi.
paddusb/paddusw: idem en unsigned
 
psubsb/psubsw : soustraction + saturation
psubusb/psubusw: idem en unsigned
 
Je ne crois pas que l'équivalent avec la mutiplication existe, de toute façon, ça serait inutile. Sinon, en restant en C pur, le plus performant restera de toute façon l'opérateur ternaire (en tous cas pour nos supers processeurs spéculatifs modernes).
 
cr = (cr > 255)? 255 : cr;
 
Par contre, je sais pas si j'ai bien compris, mais le fait passer votre cr en unsigned ne permettra pas de récupérer 0 à la place d'un nombre négatif, hein ?


 
tout à fait, mais j'ai peur qu'un  
cr = (cr > 255)? 255 : cr;  
soit moins trivial à traduire par un mov conditionnel (dans le cas d'un x86, et peut-être d'autres archi qui peuvent avoir des prédicats sur certaines instructions) de la part du compilo que le bête  
if( cr > 255 ) cr = 255;
 
dans tous les cas, il faut regarder le code produit, et choisir les options de compilation offrant le plus de possibilitées au compilateur.  
le mov conditionnel date du ppro, le sse du p3, maintenant si il travaille sur 3 canaux rgb (ou 4 - argb), faire tout l'innerloop en sse, forcément ça le fera mieux.
 
 

Reply

Marsh Posté le 30-11-2007 à 00:55:46    

SICKofitALL a écrit :

>Taz
 
Oui mais là je baigne dans le flou :D
Ca voudrait dire que si je fais :

Code :
  1. unsigned char v1 = 255;
  2. unsigned char v2 = 2;
  3. unsigned char r = 0;
  4. r = v1 + v2;


donc r va être égal à 255 ou alors au "reste", càd 1 ? :??:


 
pour moi et ma machine 1.
maintenant je sais pas du tout si dans la norme du C y'a un spécification pour les overflows. (et si y'a des archis qui ont un comportement différent en overflow entier)

Reply

Marsh Posté le 30-11-2007 à 01:11:46    

J'ai trouvé ca sur un site, jvais tester :

Code :
  1. unsigned char clamp_upper_to_255 (int i)
  2.   {
  3.     return (unsigned char) (((255 - i) >> 31) | i);
  4.   }


 
c censé limiter les valeurs à la fourchette 0 -> 255, et sans branching donc


---------------
We deserve everything that's coming...
Reply

Marsh Posté le 30-11-2007 à 01:44:01    

Reply

Marsh Posté le 30-11-2007 à 10:23:55    

SICKofitALL a écrit :

J'ai trouvé ca sur un site, jvais tester :

Code :
  1. unsigned char clamp_upper_to_255 (int i)
  2.   {
  3.     return (unsigned char) (((255 - i) >> 31) | i);
  4.   }


 
c censé limiter les valeurs à la fourchette 0 -> 255, et sans branching donc


 
tout à fait :)
mais dump la sortie assembleur de l'expression classique.

Reply

Marsh Posté le 30-11-2007 à 11:12:16    

Bon d'un point de vue vitesse de traitement, on y gagne c clair :)
 
Par contre ca "reboucle" :D
Comparez ca à l'image plus haut :
http://www.oldskoul.com/upload/FastUpload/90dffc3368cf5ae41c6db03390b8a681.jpg
 
Mais bref c un bon début !
Voila l'asm correspondant tel que VC++ Express me le sort :

Code :
  1. nnx = (unsigned char)((nx >> 31) | nx);
  2. 00411D83  mov     eax,dword ptr [nx]
  3. 00411D86  shr      eax,1Fh
  4. 00411D89  or       eax,dword ptr [nx]
  5. 00411D8C  mov    byte ptr [nnx],al


 
Working on it ! :jap:


---------------
We deserve everything that's coming...
Reply

Marsh Posté le 30-11-2007 à 11:37:13    

ARGHHH damned !
J'ai activé les optimisations dans les props du compilo de VC++, juste pour voir, et :
http://www.oldskoul.com/upload/FastUpload/db064cf461c39cf3ee86a4218adc3a85.jpg
 
Donc mon clamp à la one again plus haut est sympa pour l'ego, mais moins rapide en fait :/
 
pénible ca :pfff:


---------------
We deserve everything that's coming...
Reply

Marsh Posté le 30-11-2007 à 15:24:50    

normalement il devrait y avoir moyen de faire un:
mov ebx,255
cmp eax,ebx
cmova eax,ebx
 
http://www.sesp.cse.clrc.ac.uk/htm [...] h/vc35.htm
 
a priori y'a pas de de variantes avec un immédiat.

Reply

Marsh Posté le 30-11-2007 à 15:26:08    

SICKofitALL a écrit :

Bon d'un point de vue vitesse de traitement, on y gagne c clair :)
 
Par contre ca "reboucle" :D
Comparez ca à l'image plus haut :
http://www.oldskoul.com/upload/Fas [...] b8a681.jpg
 
Mais bref c un bon début !
Voila l'asm correspondant tel que VC++ Express me le sort :

Code :
  1. nnx = (unsigned char)((nx >> 31) | nx);
  2. 00411D83  mov     eax,dword ptr [nx]
  3. 00411D86  shr      eax,1Fh
  4. 00411D89  or       eax,dword ptr [nx]
  5. 00411D8C  mov    byte ptr [nnx],al


 
Working on it ! :jap:


 
ça reboucle quand la valeur deviens négative (au lieu d'être clampée à 0, elle l'est à 255).

Reply

Marsh Posté le 30-11-2007 à 16:06:06    

et un petiti coup de SSE2 ?

Reply

Marsh Posté le 30-11-2007 à 18:42:13    

tout à fait, dave_tetehi a proposé une solution sse.
 
mais quitte à faire du sse, autant retoucher toute la routine de bump.
 
de toutes manières, dans l'ordre (d'efficacité et d'incompatibilité, enfin d'un point de vue x86):
- hacks binaires
- cmp/cmov
- sse
 
enfin après faut bencher, yakafaucon :)
 

Reply

Marsh Posté le 30-11-2007 à 19:30:36    

perso, c'ets C à la main -> SSE2 direct. Les hacks à deux balles, c'etait fun en 1880. Maintenant c'est bon :o
 
Pareil c'est SSE2 voir SSE3+. MMX c'ets pour le jardin d'enfants.


Message édité par Joel F le 30-11-2007 à 19:31:22
Reply

Marsh Posté le 30-11-2007 à 20:21:07    

Petite précision sur le clamp(), si vous voulez vraiment borner entre 0 et 255 (et pas juste 255), il faudra faire:

Code :
  1. return (unsigned char) ((255 - i) >> 31) | (~(i >> 31) & i);


Mais ce genre de chose, c'est un peu quand même de la prog de grand-père.

Reply

Marsh Posté le 30-11-2007 à 21:04:53    

calculer en short puis faire un ?: c'ets pas genre 10x ce qu'il faut ? [:dawa] ou j'ai loupé un wagon ?

 

@dave_tetehi : ca fait bien des années que SSE2 ca se programme plus en assembleur :o

Message cité 1 fois
Message édité par Joel F le 30-11-2007 à 21:07:05
Reply

Marsh Posté le 19-12-2007 à 19:01:43    

Joel F a écrit :

ca fait bien des années que SSE2 ca se programme plus en assembleur :o


Ca se programme avec quoi ?  :??:


Message édité par sligor le 19-12-2007 à 19:02:24
Reply

Marsh Posté le 19-12-2007 à 19:10:28    

he, les intrinsics INTEL supportés par Visual Studio, gcc post-3.4 et ICC sont pas fait pour les chiens :p

 

sinon y a des outils qui masquent tt ça anyway (:o @ sig)

Message cité 1 fois
Message édité par Joel F le 19-12-2007 à 19:11:09
Reply

Marsh Posté le 19-12-2007 à 19:31:18    

Joel F a écrit :

he, les intrinsics INTEL supportés par Visual Studio, gcc post-3.4 et ICC sont pas fait pour les chiens :p
 
sinon y a des outils qui masquent tt ça anyway (:o @ sig)


Pour les outils Intel, je suis d'accord les intrinsics ça peut etre interressant, pour GCC le code généré est parfois mal optimisé par rapport à du vrai code écrit à la main. Visual Studio... jamais essayé.
 
Et puis de toute façon écrire avec les intrinsics demande de connaître le jeux d'instruction qu'il y a derrière.  
Donc ça demande autant de compétence que d'écrire directement en assembleur.
Ca dépend de l'algorithme à optimiser, mais s'il est crucial pour le programme il vaut peut être mieux coder directement en asm.
 
Sinon les outils qui masquent tout, soit ils sont pas trés bon, soit ils sont professionnels et coûtent cher ($$$).

Message cité 1 fois
Message édité par sligor le 19-12-2007 à 19:31:53
Reply

Marsh Posté le 19-12-2007 à 20:13:35    

sligor a écrit :


Pour les outils Intel, je suis d'accord les intrinsics ça peut etre interressant, pour GCC le code généré est parfois mal optimisé par rapport à du vrai code écrit à la main. Visual Studio... jamais essayé.


blablabla, ca date de quand ta dernière tentative :o
 

sligor a écrit :


Et puis de toute façon écrire avec les intrinsics demande de connaître le jeux d'instruction qu'il y a derrière.  
Donc ça demande autant de compétence que d'écrire directement en assembleur.
Ca dépend de l'algorithme à optimiser, mais s'il est crucial pour le programme il vaut peut être mieux coder directement en asm.


Non, c'ets comme si tu disais que utilisé cos et sin pr faire de la trigo c'etait naze.
Vois le sintrinsic comme des fonctiosn et ca rulez
 

sligor a écrit :


Sinon les outils qui masquent tout, soit ils sont pas trés bon, soit ils sont professionnels et coûtent cher ($$$).


Tu dis du caca :o

Reply

Marsh Posté le 19-12-2007 à 20:22:43    

Il y a quoi comme outils bien et gratuits pour vectoriser automatiquement du code C standard?

Reply

Marsh Posté le 19-12-2007 à 20:44:30    

bn c'est fou ce qu'on peut etre largué en quelques mois sur les nouvelles technos, je viens de faire des tests gcc c'est grandement amélioré en vectorisation, mea culpa.
Sinon s'il y a d'autre outils mieux que gcc et gratuits/libres je suis preneur

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed