IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Optimisation code SSE sous C++


Sujet :

C++

  1. #1
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Points : 460
    Points
    460
    Par défaut Optimisation code SSE sous C++
    Salut

    J'utilise habituellement les fonctions intrasics d'Intel pour optimiser du code C++ avec des instructions SSE.
    Mais en regardant le code généré en mode release par mon compilo (ICL) , j'ai remarqué que ce n'est pas optimal pour la vitesse sur un point particulier:
    Quand une constante est utilisée à plusieurs reprises, le compilo la garde dans un registre SSE pour la réutiliser.

    Par exemple, pour ce code simple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    a=_mm_mul_ps(a,_mm_set_ps1(10))
    b=_mm_mul_ps(b,_mm_set_ps1(10))
    le compilo génère qqch du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    movaps      xmm7,xmmword ptr [KERNEL32_NULL_THUNK_DATA+90h (44C220h)] // charge la constante
    mulps       xmm0,xmm7 // xmm0=xmm0*xmm7
    mulps       xmm1,xmm7 // xmm1=xmm1*xmm7
    Je souhaiterais obtenir:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    mulps       xmm0,xmmword ptr [KERNEL32_NULL_THUNK_DATA+90h (44C220h)]
    mulps       xmm1,xmmword ptr [KERNEL32_NULL_THUNK_DATA+90h (44C220h)]
    La stratégie du compilo est peut-être généralement payante, mais dans mon cas c'est plus lent. J'en suis sûr, car quand je modifie dans mon code C++ les valeurs des constantes pour n'en avoir aucune identique, le code généré est plus rapide (mais erroné...).
    Mon explication: + de place dans les registres => + de résultats temporaires sont gardés dans les registres => - d'aller retour avec la pile => + rapide

    Question: comment forcer mon compilo à compiler à ma manière.
    Peut-être en insérant de l'assembleur? Mais je n'ai pas réussi! (Je suis nul en assembleur)
    Il me faudrait écrire qqch dans le genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    __asm mulps a, 10;
    __asm mulps b, 10;
    J'ai a priori 2 problèmes:
    - je crois pas qu'il soit possible d'utiliser des variables directement avec __asm
    - je ne vois pas comment aller charger la bonne valeur de constante

  2. #2
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Essaie peut-être une directive #pragma optimize, pour voir si tu peux désactiver ce comportement temporairement ?

    Pour l'inclusion d'assembleur, c'est quand même peu pertinent je pense : d'une part, côté portabilité (y compris vers la prochaine version de compilateur), c'est un nid à problèmes. D'autre part, tu vas certes générer exactement le code désiré, mais tu vas également te priver de possibles améliorations du code.

    Côté valeur de constante, je n'ai pas la syntaxe exacte en tête mais l'ASM inline peut accéder aux adresses / contenus de variables C++ sans problèmes. Tu pourras donc charger l'adresse de _mm_set_ps1(10) dans une variable / registre, puis l'utiliser. Direction MSDN pour les détails.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  3. #3
    Membre confirmé Avatar de Lavock
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 560
    Points : 633
    Points
    633
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    . Direction MSDN pour les détails.
    Je veux pas paraître tatillon, mais ne posait-il pas la question pour ICC oO ?

    D'après les dire d'intel, tu devrait même pas avoir à te soucié de ça, le compilateur prenant en charge non-seulement SSE jusqu'à SSE4, mais est surtout sensé disposé d'auto-vectorisation (source Wikipedia) !

    "Parce qu'avec ICC, j'ai pas besoin de savoir que le SSE existe pour l'utiliser correctement !" <= Fausse pub. Mais c'est en partie l'esprit du compilateur.
    The mark of the immature man is that he wants to die nobly for a cause, while the mark of the mature man is that he wants to live humbly for one.
    --Wilhelm Stekel

  4. #4
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Points : 460
    Points
    460
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Essaie peut-être une directive #pragma optimize, pour voir si tu peux désactiver ce comportement temporairement ?
    J'ai essayé en désactivant l'optimisation globale (la seule option dans la liste qui aurait pu correspondre...) sur le code en question mais ca n'a rien changé

    Pour l'inclusion d'assembleur, c'est quand même peu pertinent je pense : d'une part, côté portabilité (y compris vers la prochaine version de compilateur), c'est un nid à problèmes.
    C'est clair que c'est pas top. Mais ca serait pas si problématique que ça: j'aurais bien programmé une petite fonction inline. Ca aurait donné l'impression d'utiliser une intrasic.
    Mais je bute clairement sur la syntaxe à utiliser...

    D'après les dire d'intel, tu devrait même pas avoir à te soucié de ça, le compilateur prenant en charge non-seulement SSE jusqu'à SSE4, mais est surtout sensé disposé d'auto-vectorisation (source Wikipedia) !

    "Parce qu'avec ICC, j'ai pas besoin de savoir que le SSE existe pour l'utiliser correctement !" <= Fausse pub. Mais c'est en partie l'esprit du compilateur.
    Ca c'est clairement exagéré. Aucune chance de faire plus efficace qu'avec les intrasics. Pour des cas simplissimes à la rigueur et encore... car y'a trop de problèmes à considérer pour qu'un compilo puisse en tirer pleinement parti.
    Je pense que c'est tout juste un plus pour ceux qui ne veulent pas savoir comment SSE marche: ils ajoutent l'option de compilation et espèrent un petit gain en vitesse.

  5. #5
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par Lavock Voir le message
    Je veux pas paraître tatillon, mais ne posait-il pas la question pour ICC oO ?
    Tu as raison sur le principe, sauf qu'à moins que ça n'aie changé, les deux compilateurs sont interchangeables... ICL.EXE "remplace" CL.EXE, et le code spécifique VS/CL compile sans sourciller avec le compilateur Intel. La différence se situe au niveau des performances obtenues, mais pas au niveau des sources ou des directives de compilation.

    Citation Envoyé par Charlemagne Voir le message
    J'ai essayé en désactivant l'optimisation globale (la seule option dans la liste qui aurait pu correspondre...) sur le code en question mais ca n'a rien changé
    Mouais... Donc, inutile de poursuivre sur la piste d'un #pragma.

    Citation Envoyé par Lavock Voir le message
    C'est clair que c'est pas top. Mais ca serait pas si problématique que ça: j'aurais bien programmé une petite fonction inline. Ca aurait donné l'impression d'utiliser une intrasic.
    Mais je bute clairement sur la syntaxe à utiliser...
    Tu as regardé la doc de l'assembleur inline ? Il y a des exemples d'utilisation de variables / fonctions C/C++ dans le code ASM, normalement...
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  6. #6
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Points : 460
    Points
    460
    Par défaut
    Tu as regardé la doc de l'assembleur inline ? Il y a des exemples d'utilisation de variables / fonctions C/C++ dans le code ASM, normalement...
    J'ai trouvé sur internet pas mal d'exemples SSE en assembleur, mais aucun qui ressemble à ce que je voudrais faire.
    Dans l'ensemble, ca se borne à implémenter des fonctions prenant des pointeurs d'entrées et des pointeurs de sorties.

    Je ne sais toujours pas utiliser une variable locale en assembleur.
    Ce code ne compile pas: (error: __asm 'mulps' syntax error: variable reference)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
          __m128 x=_m_set_ps1(10);
          __m128 y=_m_set_ps1(20);
          __asm mulps x y
    Et je vois encore moins quelle valeur de pointeur donner en deuxième opérande pour le chargement de constante. J'ai l'impression que le compilo se crée une table de constantes, ce qui lui permet d'écrire des chose du genre.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    mulps       xmm0,xmmword ptr [KERNEL32_NULL_THUNK_DATA+90h (44C220h)] // xmm0=xmm0*10
    PS: J'arrive en partie à mes fins en utilisant une constante et son opposé, puis en échangeant les signes + et - dans les calculs. Le compilo est bluffé. mais le code C++ est moche...

  7. #7
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Tu as essayé en tentant de lui donner une adresse (et non pas une valeur) comme argument, c'est à dire un __m128* ?
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  8. #8
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Points : 460
    Points
    460
    Par défaut
    Ca ne marche pas non plus.

    Ceci dit, je suis en train de me rendre compte que ce genre d'optimisation est un peu hasardeux. Parfois ca fait gagner un peu en vitesse, parfois pas...
    Mais ca montre bien que les compilos sont (un peu) améliorables...

  9. #9
    Futur Membre du Club
    Inscrit en
    Janvier 2010
    Messages
    8
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 8
    Points : 9
    Points
    9
    Par défaut
    Pour ce qui est de l'assembleur en ligne:
    lorsque tu utilises une variable, ça revient à une référence mémoire. Les registres eux-mêmes doivent être utilisés explicitement.
    Si je veux multiplier x et y entre eux, puis stocker le résultat dans "resultat", je dois y aller avec:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    __m128 x =_m_set_ps1(10);
    __m128 y =_m_set_ps1(20);
    __m128 resultat;
    __asm movaps xmm0, x
    __asm mulps  xmm0, y
    __asm movaps resultat, xmm0
    edit: Quant aux optimisations du compilateur, elles correspondent à un cas général. Il y a forcément des cas où ça ne sera pas le plus efficace, mais seule une optimisation guidée par profilage pourra les détecter et les traiter (et je ne sais pas si ICC propose un tel niveau d'optimisation - VC++ le fait, et GCC peut-être).

    re-edit: et en ce qui concerne le fait de forcer le compilateur à utiliser la forme "op reg, mem" plutôt que "load reg, mem / op reg, reg"...
    C'est un combat permanent et souvent vain. En tout cas, je n'ai toujours pas trouvé comment faire avec VC++ 2005.

  10. #10
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Points : 460
    Points
    460
    Par défaut
    Content de voir que je suis pas le seul à avoir remarqué cette subtilité dans le code généré.

    J'utilise ICL sous Windows. Je fait la distinction avec ICC sous Linux (que je n'ai jamais utilisé), même si j'imagine que c'est plus ou moins la même chose sous le capot.
    J'utilise pas de profiler, je me contente de lancer des benchs pour comparer les vitesses au fur et à mesure que je modifie le code. J'ai bien VTune à ma disposition, mais je sais à peine l'utiliser, et je ne suis pas sûr que ça me serve dans le cas présent.

    ICL semble utiliser systématiquement "op reg, mem" quand la constante n'est utilisée qu'une seule fois. Mais dès 2 fois, il la charge au préalable dans un registre.
    J'arrive dans une certaine mesure à forcer la main à ICL en utilisant chaque constante et son opposé. (puis en échangeant des opérations + et - dans les calculs); ça me fait gagner quelques % en vitesse.
    Mais rien ne me dit qu'un autre compilo (GCC...) en ferait de même...

  11. #11
    screetch
    Invité(e)
    Par défaut
    essaye avec AMD CodeAnalyst, il peut aussi lancer des simulations et te montrer ce qui se passe dans le processeur.
    Je suis très dubitatif des optimisations a l'aveuglette comme ca...

  12. #12
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Points : 460
    Points
    460
    Par défaut
    Citation Envoyé par screetch Voir le message
    essaye avec AMD CodeAnalyst, il peut aussi lancer des simulations et te montrer ce qui se passe dans le processeur.
    Je suis très dubitatif des optimisations a l'aveuglette comme ca...
    Je n'ai pas CodeAnalyst.
    Mais VTune doit bien être capable de faire ça aussi...

  13. #13
    screetch
    Invité(e)
    Par défaut
    CodeAnalyst est gratuit et un peu plus intuitif que VTune, bien que moins puissant je crois.

Discussions similaires

  1. utilisation d'un lecteur de code barre sous delphi
    Par bm10 dans le forum Composants VCL
    Réponses: 1
    Dernier message: 19/11/2005, 20h05
  2. [plugin][code barre] Codes Barres sous Eclipse
    Par eudes dans le forum Eclipse Java
    Réponses: 1
    Dernier message: 08/11/2005, 14h44
  3. Optimisation requete avec sous-requetes multiples
    Par gege.boubou dans le forum Requêtes
    Réponses: 3
    Dernier message: 08/09/2005, 10h42
  4. [JavaComm]Pb avec l'execution d'un code natif sous linux
    Par seb31 dans le forum Entrée/Sortie
    Réponses: 7
    Dernier message: 02/06/2004, 14h25
  5. Optimisation du swap sous Fedora Core 2
    Par demeuremichel dans le forum Administration système
    Réponses: 3
    Dernier message: 20/02/2004, 00h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo