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

x86 32-bits / 64-bits Assembleur Discussion :

[SSE] Division nombres entiers


Sujet :

x86 32-bits / 64-bits Assembleur

  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Mars 2006
    Messages
    400
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Mars 2006
    Messages : 400
    Points : 562
    Points
    562
    Par défaut [SSE] Division nombres entiers
    Mon problème est le suivant, je n'arrive pas à trouver d'instruction SSE pour diviser des nombres entiers.

    Les instructions que j'ai trouvé sont divss et dicps, mais ces instructions manipulent des float.

    En fait, j'ai besoin de division entières pour faire des interpolations liéaires.
    a1*i/d
    a2*i/d
    a3*i/d
    a4*i/d
    J'ai plusieurs a (a1,a2,a3,...,an) que je multiplie par un même i puis divise le résultat par un même d.
    a, i et d étant tous les 3 des entiers, je fait une multiplication entière suivie du division entière.
    Je ne veux pas manipuler de flottants car les instructions seront plus lentes et je perdrait en précision.

    Pour le moment, j'utilise les instructions mul et div qui utilisent les registres généraux.
    Je voudrais utiliser les instructions SSE pour pouvoir effectuer plusieurs calculs en parallèle.
    Mais je n'ai pas trouvé d'instruction pour faire des divisions entières en SIMD.

    Quelqu'un aurait-il une solution ?

  2. #2
    Inactif
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 130
    Points : 160
    Points
    160
    Par défaut
    Manipuler des entiers n'est pas vraiment le rôle/but de SSEx. (ça tu le sais déjà )
    Pour faire du SIMD (Single Instruction Multiple Data pour les lecteurs), tu auras plus de chance(s) avec MMX (avec quelques restrictions bien-sûr...).

    "Je ne veux pas manipuler de flottants car les instructions seront plus lentes et je perdrait en précision. "

    Je ne comprends pas bien le problème de précision ?

    Le "problème de vitesse" peut être réglé par l'effet de // des deux processeurs.

    Si tu acceptes le travail en flottant tu peux traiter ton problème de division de manière plus rapide.

  3. #3
    Membre confirmé
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Mars 2006
    Messages
    400
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Mars 2006
    Messages : 400
    Points : 562
    Points
    562
    Par défaut
    Je ne comprends pas bien le problème de précision ?
    Avec des nombres entiers,
    Lorsque l'on a un nombre 16 bits que l'on multiplie par un nombre 16 bits, on obtient un nombre 16 bits.

    Avec des flottants 32 bits, on a 1 bit de signe, 23 pour la mantisse et 8 pour l'exposant. Chaque nombre a donc une précision de 23 bits.
    Avec ces 23 bits, on peut stocker nos nombres de 16 bits.
    Par contre, le résultat 32 bits de la multiplication ne peut être stocké dans son intégralité : on perd 9 bits. D'où une perte de précision.


    Concernant mon problème, j'ai peut-être une solution : utiliser des nombres à virgule fixe.
    Je rappelle qu'il s'agit de faire des interpolations de type a*i/d.
    a est une constante 16 bits,
    i est une variable 16 bits,
    d est une constante 16 bits.

    Je vais d'abords convertir a en 32 bits par décalage à gauche de 16 bits.
    Je vais diviser la valeur par d.
    Ensuite, pour l'interpolation, je n'aurait qu'une multiplication à faire.
    J'aurais donc :
    - une variable k=(a shl 16)/d initialiser au début du programme
    - une multiplication i*k dans ma boucle

    Comme ne n'aurais plus de division, je pourrais utiliser les instructions SSE pour effectuer 4 interpolations en même temps.

    Je vais tester cette solution.

  4. #4
    Inactif
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 130
    Points : 160
    Points
    160
    Par défaut
    Il me semble que quelques erreurs se sont glissées dans ton raisonnement sur la précision FPU.


    Il est possible, pour limiter les erreurs, d'arrondir des floats au dernier moment (l'équivalent FPU étant fld ..... fistp) tout le calcul étant fait au format que tu as programmé pour la FPU...


    Plutôt que faire des décalages (coûteux) tu peux utiliser des décalages d'adresse qui te permettent de travailler sur le MSDWord (partie entière) et LSDWord partie décimale en évitant toutes les rotations intermédiaires inutiles. C'est un init, certes, mais on ne se refait pas... De plus tu pourrais ainsi travailler avec un format de nombre maison (integer). Par expérience je peux te dire que c'est très efficace et t'évite les pénalisations d'accès d'adresses par simples masques.

    Il me semble que pour ce genre de traitements MMX est plus indiqué... mais bon...rom(e)..les chemins

  5. #5
    Membre confirmé
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Mars 2006
    Messages
    400
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Mars 2006
    Messages : 400
    Points : 562
    Points
    562
    Par défaut
    Il est possible, pour limiter les erreurs, d'arrondir des floats au dernier moment (l'équivalent FPU étant fld ..... fistp) tout le calcul étant fait au format que tu as programmé pour la FPU...
    Je n'utilise pas la FPU mais SSE.
    Le type de données que j'utilise est Packed DWord.
    Avec des float 32 bits, on a 23 chiffres significatifs.
    Avec des entiers 32 bits, on a 32 chiffres significatifs.

    Avec des entiers, je peux donc avoir des nombres à virgule fixe avec 16 chiffres avant la virgule et 16 chiffres après la virgule.
    Avec des float, j'aurais 16 chiffres avant la virgule et seulement 7 chiffres après la virgule.
    Il y a 23 chiffres significatifs pour les float contre 32 pour les integer.

    Donc, à taille égale, les entiers sont plus précis que les flottants.

    Concernant fild et fistp,
    Lorsque l'on charge dans la FPU des nombres 32 bits (ou tout autre nombre), ils sont convertis en Double Extended Precision (flottants sur 80 bits).


    Pour comparer la précision de plusieurs formats de données, il faut prendre des formats de données de taille égale.
    IL est évident que des float 80 bits sont plus précis que des integer 32 bits. Mais en comparant des float 32 bits avec des integer 32 bits, on voit que les entiers sont plus précis que les flottants.
    "On ne peut comparer que des éléments comparables."


    Pour vérifier que les entiers sont plus précis que les flottants, voici une expérience :
    - prendre un nombre entier
    - le charger dans la FPU
    - extraire le float correspondant
    - charger ce float
    - extraire le nombre entier

    Voici le code correspondant :
    finit
    fild val1
    fstp f
    fld f
    fistp val2

    val1 et val2 sont des entier 32 bits.
    Si les flottants sont plus précis que les entiers, val2 doit être égale à val1.

    Voici un exemple :
    val1 : 1234567890
    val2 : 1234567936

    voici les représentations binaires de val1 et val2
    val1 : 1001001100101100000001011010010
    val2 : 1001001100101100000001100000000

    On voit que la FPU n'a conservé que 23 chiffres de val2
    Le 24ième bit de val2 correspond à l'arrondi du dernier octet de val1.
    Le dernier octet de val2 a tous ses bits à 0.

    Lorsque l'on convertit des entiers en flottants pour les retransformer en entiers, on perd 8 chiffres du nombre.
    Les entiers 32 bits sont donc plus précis que les flottants 32 bits.



    Il me semble que pour ce genre de traitements MMX est plus indiqué
    Les registres MMX sont des registres 64 bits.
    Les registres XMM (SSE) sont des registres 128 bits.
    Comme je traite des nombres 32 bits,
    avec MMX, je peux traite 2 nombres (Packed DWord) en même temps,
    avec SSE, je peux traiter 4 nombres (Packed DWord) en même temps.

    Avec SSE, on traite 2 fois plus de données en même temps qu'avec MMX. Donc le code est 2 fois plus rapide.


    Comme je l'ai dit plus haut,
    je vais utiliser des nombres entiers 32 bits au format suivant :
    - les 16 bits de poids forts pour la partie entière
    - les 16 bits de poids faibles pour la partie décimale.

    Pour traiter l'opération suivante a*i/d,
    Avec des entiers : a*i/d
    un nombre 16 bits * un nombre 16 bits = un nombre 32 bits
    32 bits divisés par 16 bits = 16 bits

    Avec des entiers à virgule fixe : a/d*i
    un nombre 16 bits étendu sur 32 bits
    32 bits divisés par 16 bits = 16 bits
    16 bits * 16 bits = 32 bits
    on obtient un entier sur 16 bits (+ 16 bits pour la partie décimale)

    Du début à la fin du calcul, on a toujours au moins 16 bits significatifs. On ne perd donc aucun chiffre.

  6. #6
    Inactif
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 130
    Points : 160
    Points
    160
    Par défaut
    Je parle d'équivalent FPU (cas de MMX qui est plus que proche et pas que par les registres) pour simplifier l'exposition du problème de précision et non pour exprimer une solution équivalente :

    Comparer ce qui est comparable, justement c'est là qu'il y a malaise et pas des moindres :

    En simplifiant tu sembles vouloir affirmer qu'il est impossible de faire des calculs sur des nombres > 23 bits avec la FPU (résolution fixe en float quoi…)!!!
    C'est quelques scientifiques qui vont être contents d'apprendre que : +8388607 /-8388608 valeurs maxi pour le FPU ?
    Non tu rigoles sur ce coup ou c'est juste pour comparer du "comparable" ?

    Il faut que tu précises la précision, justement, et c'est loin d'être fait.

    Le format de travail, et de nombreux paramètres de la FPU sont programmables, comme pour tout autre co-processeur, heureusement pour nous.

    Merci pour le bout de code mais tu ne mesureras rien de sérieux avec ça...
    J'ai déjà donné pour te dire que cette méthode est complètement bidon au niveau de ce que tu tentes de mesurer. Dès que tu fais des échanges integer/float/integer les erreurs d'arrondis/troncatures étant paramétrables et de plus cumulées tu n'obtiens rien de "réel" (c'est le cas de la dire arf) au niveau de tes mesures... Sinon tous les calculs utilisant la FPU présenteraient des faiblesses nettement plus élevées que celles constatées ces dernières années et qui serait plus qu’un simple bug (re arf). Il y en a, mais loin des proportions que tu indiques avec ce système de mesure totalement inadéquat.

    Avec une virgule fixe signée, tu risques de rencontrer quelques problèmes non seulement de résolution mais aussi de format +32767 -32768. Tu ne pourras pas multiplier des nombres WORD signés par exemple sans dépassements et repliements intempestifs. De plus la bidouille utilisée est n’importe comment pénalisante en accès (WORD au lieu de DWORD tu bouffes du CPU pour rien de plus)

    Avec SIMD, plus qu'ailleurs, l'ordre du code et la longueur de ce dernier ne sont pas liés aux performances : Le code le plus ramassé est rarement le plus rapide et pour de nombreuses raisons (ne serait-ce que par simple déroulement, c’est ce que nous vérifions tous les jours).

    Pour la vitesse de code obtenu, je te rappelle que ce n'est pas le code le plus concis qui tourne le plus vite, surtout en utilisant cette technologie. La // des opérations et les anticipations/prédictions compliquent le vieux principe du comptage de cycles... l'optimisation n'est plus « linéaire ».

    Ceci dit, rien ne remplace les essais grandeur nature et la vérification de l’exactitude de tes outils/méthodes de mesures. Il est cependant dommage que nous ne bénéficiions pas des outils de type A = A+B*C etc. Le recours aux bidouilles reste hélas la plupart du temps de mise.


    Bon code

  7. #7
    Membre confirmé
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Mars 2006
    Messages
    400
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Mars 2006
    Messages : 400
    Points : 562
    Points
    562
    Par défaut
    En simplifiant tu sembles vouloir affirmer qu'il est impossible de faire des calculs sur des nombres > 23 bits avec la FPU (résolution fixe en float quoi…)!!!
    C'est quelques scientifiques qui vont être contents d'apprendre que : +8388607 /-8388608 valeurs maxi pour le FPU ?
    Tu as mal compris ce que j'ai dit.

    Avec la FPU, on peut manipuler 3 types de float :
    - des Single Precision
    length : 32 bits
    precision : 24 bits
    range : 2^-126 à 2^127
    range : 1.18x10^-38 à 3.40x10^38

    - des Double Precision
    length : 64 bits
    precision : 53 bits
    range : 2^-1022 à 2^1023
    range : 2.23x10^-308 à 1.79x10^308

    - des Double Extended Precision
    length : 80 bits
    precision : 64 bits
    range : 2^-16382 à 2^16383
    range : 3.37x10^-4932 à 1.18x10^4932

    Pour simplifier, je ne compare que les Single Precision aux Integer 32 bits.
    On pourrait également comparer les Double Precision aux Integer 64 bits, ainsi que les Double Extended Precision aux Integer 80 bits.

    Prenons donc les Single Precision.
    On a des nombres codés sur 32 bits avec 1 bit de signe, 23 bits pour la mantisse et 8 bits pour l'exposant.
    On a une précision de 24 bits. Le premier étant toujours 1, on a donc +8388607 /-8388608 valeurs différentes pour la mantisse.
    Tu semble contester ces chiffres. Pourtant, ils viennent de la doc Intel et on retrouve les mêmes dans la doc AMD.
    Alors est-ce que ces chiffres sont justes ? Ou est-ce que c'est la doc qui est fausse ?

    +8388607 /-8388608 valeurs différentes pour la mantisse ne signifie pas qu'il n'y a que +8388607 /-8388608 valeurs différentes pour les flottants.
    En float 32 bits, il y a bien 2^32 valeurs différents. Mais parmis ces 32 bits, 8 codent l'exposant et 1 code le signe. Il reste donc bien 23 bits pour la mantisse. 2^23 est bien égale à 8388608. Non ?

    Il faut que tu précises la précision, justement, et c'est loin d'être fait.
    La précision d'un nombre est le nombre de chiffres utilisés pour écrire ce nombre.
    exemples :
    nombre : precision
    123 : 3
    2.3 : 2
    54.768 : 5

    Dans le cas des flottants 32 bits, les chiffres du nombre, c'est la mantisse (23 bits).
    Les 8 bits de l'exposant indiquent la position de la virgule.
    Tous les floattants 32 bits sont codés ainsi :
    (-1)^S*1.MMMMMMMMMMMMMMMMMMMMMMM*2^EEEEEEEE
    avec S le signe, M la mantisse, et E l'exposant

    Les flotttants 32 bits ont une précision de 24 bits.
    Les entiers 32 bits ont une précision de 32 bits.
    Es-tu d'accord avec ces chiffres ?
    Tu vois donc bien que les flottants sont moins précis que les entiers.
    Cette différence de précision est dûe au fait que les flotants doivent contenir l'emplacement de la virgule.

    En conclusion,
    les entiers sont plus précis que les flottants (à taille égale).
    les entiers occupent moins de place que les flottants (à précision égale).
    le traitement des entiers est plus rapide que le traitement des flottants.
    Donc, une des premières techniques d'optimisation de code est de tranformer les données flottantes en données entières pour manipuler un maximum de données entières et un minimum de données flottantes.

    Le format de travail, et de nombreux paramètres de la FPU sont programmables, comme pour tout autre co-processeur, heureusement pour nous.
    En fait, la FPU travaille en interne avec des Double Extented Precision (flottants sur 80 bits).
    La FPU travaille donc avec une précision maximale de 64 bits.
    Le plus grand type de données entier étant le QuadWord (64 bits), la FPU peut être pratiquement aussi présice que les entiers.
    Mais les Double Extended Precision occupent 12,5 % de place en plus que les QuadWord.

    Concernant MMX/SSE, le type flottant le plus grand est le Double Precision (flottants 64 bits) et le type entier le plus grand est le QuadWord.
    Les Doubles Precision ont une précision de 53 bits contre 64 bits pour les QuadWord.
    Donc MMX/SSE utilisé avec des flottants est nettement moins précis que MMX/SSE utilisé avec des entiers (les entiers sont 20,7 % plus précis que les flottants).

    Avec une virgule fixe signée, tu risques de rencontrer quelques problèmes non seulement de résolution mais aussi de format +32767 -32768. Tu ne pourras pas multiplier des nombres WORD signés par exemple sans dépassements et repliements intempestifs. De plus la bidouille utilisée est n’importe comment pénalisante en accès (WORD au lieu de DWORD tu bouffes du CPU pour rien de plus)
    Qu'entends-tu par problèmes de résolution ?

    Concernant les dépassements, je n'en ai aucun.
    J'ai des DWORD dont seul le premier WORD est utilisé (le deuxième étant bien sûr à 0) que je multiplie par un WORD.
    Le résultat est un DWORD stocké dans un DWORD : il n'y a donc aucun dépassement.

    Pour la vitesse de code obtenu, je te rappelle que ce n'est pas le code le plus concis qui tourne le plus vite, surtout en utilisant cette technologie
    Avec SSE, on n'a pas un code plus court qu'avec MMX (au contraire!).
    La plupart des instrucions SIMD peuvent s'utiliser avec les registres MMX (64 bits) ou avec les registres XMM (128 bits).
    Les instructions manipulant des registres MMX ou XMM ont le même opcode.
    Pour utiliser XMM, les instructions sont préfixées par le code 66.

    Avec SIMD, plus qu'ailleurs, l'ordre du code et la longueur de ce dernier ne sont pas liés aux performances
    Avec SIMD, on peut traiter plusieurs données en même temps.
    Par exemple, si on manupile des Packed DoubleWords :
    - avec MMX, on manipule 2 données en même temps (64/32=2)
    - avec SSE, on manipule 4 données en même temps (128/32=4)
    Donc, dans une même période de temps, on manipule 2 fois plus de données avec SSE qu'avec MMX.
    Donc, si on a un volume de données à traiter, on peut le faire en 2 fois poins de temps avec SSE qu'avec MMX.
    SSE est donc plus rapide que MMX (même si les instructions MMX et SSE sont aussi rapides).
    Il s'agit du concept SIMD (Single Instruction, Multiple Data). On réduit le temps de calcul non pas en réduisant la durée d'exécution des instructions, mais en augmentant le nombre de données manipuléesen même temps.

  8. #8
    Inactif
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 130
    Points : 160
    Points
    160
    Par défaut
    Merci pour le temps pris sur la résolution de ton problème pour me répondre.

    +8388607 /-8388608 valeurs différentes pour la mantisse ne signifie pas qu'il n'y a que +8388607 /-8388608 valeurs différentes pour les flottants.

    Tu vois donc bien que les flottants sont moins précis que les entiers.
    Cette différence de précision est dûe au fait que les flotants doivent contenir l'emplacement de la virgule.

    En conclusion,
    les entiers sont plus précis que les flottants (à taille égale).
    les entiers occupent moins de place que les flottants (à précision égale).


    Nous sommes donc en plein dans le problème de la résolution car nous ne comparons pas des entiers en float avec des entiers integer (ce qui serait absurde, (utiliser les diverses unités f pour faire de l'integer)) mais la résolution des floats sur 32 bits et celle que tu obtiendras avec une virgule fixe 32 bits. Comparer ce qui est comparable disais-tu. Mais nous sommes loin du problème initial.

    Je ne reviens pas sur la vitesse et l'optimisation de code, mon précédent post était clair. Idem sur les définitions SIMD . mais c'est cool pour les lecteurs, même si c'est largement incomplet.

    Concernant les dépassements, je n'en ai aucun.
    J'ai des DWORD dont seul le premier WORD est utilisé (le deuxième étant bien sûr à 0) que je multiplie par un WORD.
    Le résultat est un DWORD stocké dans un DWORD : il n'y a donc aucun dépassement.

    Avec des nombres entiers,
    Lorsque l'on a un nombre 16 bits que l'on multiplie par un nombre 16 bits, on obtient un nombre 16 bits.

    ( y'a pas une tîte erreur là... FFFF * FFFF = FFFF ! ? )


    En virgule fixe, multiplier les MSWORD donne un résultat DWORD (je fais abstraction du problème de signe restons simples ). Ton LSWORD étant occupé, justement, par ta partie décimale. Autrement, tu dois utiliser toute une gymnastique de rotation qui ralentie tellement le code que le simple fait d'utiliser la FPU en // au CPU est nettement plus rapide (même si la FPU c'est fait une réputation de lenteur, la // des processeurs aux dépend de la // des instructions est le principe salvateur de ces "dernières" années... même chez Atari ils avait compris, que dire du Z80...). Idem pour les croisements SSEx XMM MMX et integer CPU...

    Si ne pas le faire va plus vite (qui est un excellent principe d'optimisation) ne pas le faire ici correspond à le faire faire par un autre en même temps. , Par exemple, la copie de blocs en MMX est disqualifiée par un VSP en graphisme pour cette raison même.

    Mais bon, on s'en fout un peu tu me diras et nous ne somme pas partis pour réduire .

    Tu as trouvé une solution efficace ?
    Quelle est le but de ton interpolation ?
    Dans quel cadre ? (tableaux, graphisme, asservissement...)
    Est-ce qu'au final tu as besoin de la partie décimale ?
    Quelle est la résolution finale ?

  9. #9
    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
    Je prends votre discussion en route.
    Il est tard et j'ai pas eu le temps de tout lire.

    Voici la solution que j'avais adoptée pour un problème quelque peu similaire.
    Il s'agissait de convertir une des couleurs dans différents espaces, ce qui correspond en fait à un produit de matrice 4x4 par un vecteur. (Dans l'exemple suivant de RGBA à YUVA)
    Ne vous focalisez pas trop sur la syntaxe car j'utilise des surcharges d'opérateurs et de fonctions pour simplifier mon programme.

    J'utilise des virgules fixes.
    L'instruction madd est très pratique dans mon cas.
    J'utilise ici MMX car c'était suffisant dans mon cas (4 shorts), mais on peut faire la même chose avec SSE2.

    Conversion des chars en shorts
    Calculs
    Décalages
    J'avais fais un décalage de 14 bits seulement pour permettre des multiplications avec des entiers négatifs.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    inline YUVA::YUVColor(const RGBA &x)
    {    
      m64b m(m64i((int&)x));
      m64s m0 = unpacklo(m);
     
    //0.299*r.x+0.587*r.y+0.114*r.z
    //-0.147*r.x-0.289*r.y+0.437*r.z
    //0.615*r.x-0.515*r.y-0.100*r.z
     
      m64i Y0 = madd(m0,m64s( 9798,19235,3736,0));
      m64i Y1 = madd(m0,m64s(-4817,-9470,14320,0));
      m64i Y01 = shiftra<15>(unpacklo(Y0,Y1)+unpackhi(Y0,Y1));
     
      m64i Y2 = madd(m0,m64s(10076,-8438,-1638,    0));
      m64i Y3 = madd(m0,m64s(    0,    0,    0,16384));
      m64i Y23 = shiftra<14>(unpacklo(Y2,Y3)+unpackhi(Y2,Y3));
     
      m64s Y = packs(Y01,Y23)+m64s(-128,0,0,-128);
      m64c n = packs(Y,Y)+m64c(-128,0,0,-128);
     
      (int &)(*this) = _mm_cvtsi64_si32(n);
    }
    J'espère que ça aidera

    Erratum:
    J'avais fais des normalisations sur 14 et 15 bits pour m'assurer qu'il ne pourrait pas y avoir de dépassements de capacité dans les calculs pour le pire des cas.

    Les calculs internes sont faits sur 32 bits (integer) gràce à madd. Ce qui limite les erreurs d'arrondit après les sommes.

  10. #10
    Inactif
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 130
    Points : 160
    Points
    160
    Par défaut
    c'est ça ou les tables (grumf and arf)

  11. #11
    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
    Si tu parles de tables pour convertir les couleurs, je crois pas que ce soit la solution:
    -trop de couleurs 256^3
    -calculs rapides
    La bibliothèque gratuite "Approximate Math Library" d'Intel (téléchargeable sur leur site) est un excellent exemple de comment se débarasser des tables pour les fonctions mathématiques.

    J'ai vu l'affirmation plus haut que les calculs flottants sont aussi rapide que pour les entiers. C'est peut-être vrai à quantité égale, mais en général on peut manipuler plus d'entiers à la fois (4 ints, 8 shorts, 16 chars contre 4 floats ou 2 doubles).
    Mais le problème principal est la conversion, j'ai l'impression qu'elle est affreusement lente par rapport aux restes des calculs. Donc je considère qu'il faut faire tout son possible pour éviter les conversions, d'où l'utilité des calculs en virgule fixe.

  12. #12
    Inactif
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 130
    Points : 160
    Points
    160
    Par défaut
    Si tu parles de tables pour convertir les couleurs, je crois pas que ce soit la solution:
    -trop de couleurs 256^3
    -calculs rapides
    Ce n'est pas la solution, c'est une solution.

    256^3 dans certains cas et certaines approches .
    Je suis d'accord pour remplacer les accès tables pour des calculs, par exemple, de Sinus/Cosinus précis, ou d'intégrateurs. Effectivement, l'accès à l'information par le calcul est plus rapide dans certains cas, bien précis cependant.

    Ne pas le faire reste le plus rapide

    J'ai vu l'affirmation plus haut que les calculs flottants sont aussi rapide que pour les entiers. C'est peut-être vrai à quantité égale, mais en général on peut manipuler plus d'entiers à la fois (4 ints, 8 shorts, 16 chars contre 4 floats ou 2 doubles).
    Ce n'est pas la vitesse mais la résolution qui était visée et j'ai déjà écrit sur la // et ses facécies. Le seul problème, de cette dernière, étant les outils de mesure pour les benchmarks et le mode d'execution du code.

    Mais le problème principal est la conversion, j'ai l'impression qu'elle est affreusement lente par rapport aux restes des calculs. Donc je considère qu'il faut faire tout son possible pour éviter les conversions, d'où l'utilité des calculs en virgule fixe.
    Là je souscris. Si les DSP l'utilisent ce n'est pas pour rien. Simplement, une véritable virgule fixe, sans SAL avec une déclaration d'adresse alignée. La virgule flottante n'étant qu'une vue de l'esprit (dans le cadre de la discussion)...

    Idem pour les tests en flottant. En C/C++ c'est infernal, il n'y a qu'en ASM qu'on arrive à exploiter correctement les mécanismes. (à vos dé-assembleurs !)

  13. #13
    Membre confirmé
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Mars 2006
    Messages
    400
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Mars 2006
    Messages : 400
    Points : 562
    Points
    562
    Par défaut
    Mon problème est de tracer une droite horizontale en assignant une couleur

    différente à chacune de ses extrémités.
    Pour chacun des points de la droite, il faut donc interpoler sa couleur à partir de la couleur de chaque extrémité de la droite.

    Voici le prototype de ma fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    draw3DHorizontale PROTO near C,
        ddsd:DWORD,
        x1:DWORD,
        x2:DWORD,
        y:DWORD,
        color1:DWORD,
        color 2:DWORD
    Ma fonction utilise les variables locales suivantes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    LOCAL color16:QWORD, color322:QWORD, color32:QWORD
    color32 est une variable sur 16 octets déclarée en 2 fois avec des QWORD


    On calcule le nombre de pixels de la droite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    mov ebx,x2
    sub ebx,x1
    inc ebx
    Le résultat est stocké dans ebx.


    Une couleur est un DWORD : 1 octet par composante (ARGB).

    Je commence par calculer la variation de couleur entre color1 et color2

    (color2-color1).
    Dans les pires des cas,
    - soit que A1=0 et A2=255 => la variation de A est +255
    - soit que A1=255 et a2=0 => la variation de A est -255
    La variation ne peut être stockée sur 1 octet. Elle sera donc stockée sur 2 ocets par composante.

    Voici le code permettant de calculer la variation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    movd mm0,color1
    movd mm1,color2
    pxor mm2,mm2
    punpcklbw mm0,mm2
    punpcklbw mm1,mm2
    psubw mm1,mm0
    movq color16,mm1
    On commence par convertir chaque composante couleur (BYTE) en WORD.
    Puis on effectue la différence.
    On obtient la variation (signée) avec 2 octets par composante.


    Ensuite, on divise la variation par le nombre de pixels de la droite.
    On obtient l'incrément de la couleur qui sera utilisé dans la boucle.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    mov ecx,4
    xor edi,edi
    @@:
    mov ax,word ptr [color16+edi]
    shl eax,16
    cdq
    idiv ebx ; nombre de pixels
    mov dword ptr [color32+edi],eax
    add edi,4
    dec ecx
    jnz @B
    Chaque composante est alors codée sur 32 bits avec des nombres à virgule fixe.
    Il y a 16 bits pour la partie entière, et 16 bits pour la partie décimale.
    La composante couleur "réelle" 8 bits se trouve dans le 3ème octet de chaque nombre.
    Le format de chqaue nombre est alors :
    SSAADDDD
    DDDD désigne la partie décimale
    SSAA désigne la partie entière
    AA est la véritable valeur de la composante couleur
    SS indique le signe du nombre (une variation de +/- 255 ne peut pas être stochée sur 1 cotet seulement)


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    movq mm1,qword ptr [color32+0]
    movq mm3,qword ptr [color32+8]
     
    movq mm2,mm0
    pxor mm4,mm4
    punpcklwd mm0,mm4
    punpckhwd mm2,mm4
    pslld mm0,16
    pslld mm2,16
     
    ;** boucle **
    @@:
    ;** ... **
    ; calcul de la couleur
    paddd mm0,mm1
    paddd mm2,mm3
    ;** ... **
    dec ebx
    jnz @B
    mm0 et mm2 contiennent color1 au format SSAADDDD
    mm1 et mm3 contiennent la variation de couleur

    On a donc mm0 et mm2 qui contiennent la couleur du pixel au format SSAADDDD

    mm0 contient SSGGDDDDSSBBDDDD
    mm2 contient SSAADDDDSSRRDDDD

    Il s'agit maintenant de touver une manière rapide d'extraire AARRGGBB des registres mm0 et mm2.

  14. #14
    Inactif
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 130
    Points : 160
    Points
    160
    Par défaut
    Dans la liste des (nombreux) Bitblt (avec jeu de pattern) une simple astuce d'offset te permet de repousser dans le VSP la charge de travail sans comparaison : Code plus leger, plus rapide, plus portable, plus souple (de plus tu pourras choisir plusieurs lois de variation).
    M'enfin, c'est comme vouvoul...

  15. #15
    Membre confirmé
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Mars 2006
    Messages
    400
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Mars 2006
    Messages : 400
    Points : 562
    Points
    562
    Par défaut
    mm0 contient SSGGDDDDSSBBDDDD
    mm2 contient SSAADDDDSSRRDDDD
    Il s'agit maintenant de touver une manière rapide d'extraire AARRGGBB des registres mm0 et mm2.
    Pour extraire la couleur, j'ai écrit le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
        movq mm4,mm0
        movq mm5,mm2
        pslld mm4,8
        pslld mm5,8
        psrld mm4,24
        psrld mm5,24
        packuswb mm5,mm4
        packuswb mm4,mm5
        movd eax,mm4
    Est-il juste ?
    Peut-on faire plus rapide ?

  16. #16
    Membre confirmé
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Mars 2006
    Messages
    400
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Mars 2006
    Messages : 400
    Points : 562
    Points
    562
    Par défaut
    Mon code contenait quelques erreurs.
    Voici une version corrigée.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    draw3DHorizontale PROC near C,ddsd:DWORD, x1:DWORD,x2:DWORD, y:DWORD, color1:DWORD,color2:DWORD
        LOCAL color16:QWORD,color322:QWORD,color32:QWORD
        pushad
     
        mov ebx,x2
        sub ebx,x1
        inc ebx
     
        movd mm0,color1
        movd mm1,color2
        pxor mm2,mm2
        punpcklbw mm0,mm2
        punpcklbw mm1,mm2
        psubw mm1,mm0
        movq color16,mm1
     
        mov ecx,4
        xor edi,edi
    @@:
        mov ax,word ptr [color16+edi*2]
        shl eax,16
        cdq
        idiv ebx
        mov dword ptr [color32+edi*4],eax
        inc edi
        dec ecx
        jnz @B
     
        movq mm1,qword ptr [color32+0]
        movq mm3,qword ptr [color32+8]
     
        movq mm2,mm0
        pxor mm4,mm4
        punpcklwd mm0,mm4
        punpckhwd mm2,mm4
        pslld mm0,16
        pslld mm2,16
     
        mov esi,ddsd
        mov eax,y
        mul (draw3DDesc ptr [esi]).dwWidth
        add eax,x1
        mov esi,(draw3DDesc ptr [esi]).lpSurface
        shl eax,2
        add esi,eax
        mov eax,color1
     
    ;** boucle **
    @@:
        mov [esi],eax
     
        paddd mm0,mm1
        paddd mm2,mm3
     
        movq mm4,mm0
        movq mm5,mm2
        pslld mm4,8
        pslld mm5,8
        psrld mm4,24
        psrld mm5,24
        packuswb mm4,mm5
        packuswb mm4,mm5
        movd eax,mm4
     
        add esi,4
        dec ebx
        jnz @B
     
    ;** fin **
        emms
        popad
        ret
    draw3DHorizontale ENDP
    Je l'ai testé et il fonctionne.

    Cependant, j'ai plusieurs questions :
    - peut-on écrire un code qui soit plus rapide ? (en temps d'exécution)
    - peut-on réduire la consommation mémoire ?
    La procédure alloue 3 QWORD sur la pile,
    peut-on remplacer ces variables par des registres ?

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Implémentation d'une division entre nombres entiers avec une précision arbitraire
    Par i.Polo dans le forum Algorithmes et structures de données
    Réponses: 4
    Dernier message: 28/05/2015, 10h51
  2. division de nombres entiers
    Par Ingenieur12 dans le forum C++
    Réponses: 1
    Dernier message: 24/01/2015, 16h03
  3. Format des nombres entiers, séparateurs de milliers
    Par zazaraignée dans le forum Langage
    Réponses: 2
    Dernier message: 26/10/2005, 01h25
  4. nombre entier
    Par eleve36 dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 25/10/2005, 16h25
  5. [LG]Former un nombre entier à partir de chiffre naturel.
    Par lecanardjaune dans le forum Langage
    Réponses: 2
    Dernier message: 12/11/2003, 22h36

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