Normaliser un vecteur [Défit !] - ASM - Programmation
Marsh Posté le 26-03-2006 à 13:26:45
il y a une code similaire dans le source de quake 3, fichier "code/game/q_math.c", ligne 532
Code :
|
Citation : |
ca doit se faire assez facilement en asm
Code :
|
ca mappe l'image memoire du flottant x dans l'entier i
Citation : x = *(float*)&i |
meme chose mais dans l'autre sens
le but est de permettre d'appliquer des opérations "bit à bit" sur l'image mémoire d'un flottant
Marsh Posté le 27-03-2006 à 00:03:16
Merci Skelter de ta réponse mais elle ne m'aide pas beaucoup. J'ai bien compris qu'il y avait un bidouillage sur les bits du nombre pour obtenir la première approximation pour les itérations de l'algorithme de Newton.
Je doute pas que cela se fasse facilement en asm, j'aimerais savoir comment
A++
Lionel
Marsh Posté le 30-03-2006 à 10:16:23
Bon j'ai trouvé, cela se résume à :
mov eax, DWORD PTR [esp]
sar eax, 1
neg eax
add eax, 1597463007
mov DWORD PTR [esp+4], eax
J'ai aussi trouvé avec les instruction SSE, encore plus rapide !
Marsh Posté le 11-04-2006 à 16:21:04
Citation : J'ai aussi trouvé avec les instruction SSE, encore plus rapide ! |
Sauf que tu ne vas pas gagner grand chose si tu ne bascules pas en SoA, cad de { x,y,z,0 } à { x,x,x,x }/{ y,y,y,y }/{ z,z,z,z }; sinon tu gaspilles 1/4 des calculs.
Disons que tu persistes, il te faudra aussi user de rsqrt*s pour obtenir un quelconque gain. Vu le peu d'ulp que procurent ces instructions reciproques, il te faudra coller par dessus une iteration à la Newton-Raphson: x0 = rsqrtss(val), rsqrt = 0.5f*x0*(3.f-(val * x0²)).
Sauf qu'il y a une légere embrouille en 0 et au lieu de +/- inf tu te retrouveras avec un chouette NaN; la aussi ça se corrige sans branche aucune mais la latence continue de s'envoler.
Note: "float y; i = * ( long * ) &y;" -> aliasing -> caca.
Marsh Posté le 11-06-2006 à 16:36:32
Salut, Je me joint a la reflexion ...
J'ai trouvédu code sse qui fait (bien ?) le travail.
Code :
|
Le probleme c'est qu'il faut de je donne a manger a xmm0.
Comme 'st une fonction de la classe, j'ai codé ca:
Code :
|
Cependant je me doute bien qe c'est pas optimal mais je ne sais pas comme mette this dans xmm0 ...
Et c'est super long aussi
Allez, Salut tous
Marsh Posté le 21-11-2006 à 22:54:09
J'avais raté cette réponse.
Non, Palito, rsqrtps n'est pas à utiliser en lieu et place d'un 1/sqrt pour la bonne et simple raison que cela ne procure que ~12bits de précision. Comme indiqué plus haut, il faut 1 iteration de Newton-Raphson pour obtenir ~23bits sur la mantisse mais une implémentation naive ne recouvre pas le même domaine etc... au final, une fois tous les trous bouchés le gain est loin d'etre substantiel.
Marsh Posté le 21-11-2006 à 23:14:18
oui je me suis rendu compte que c'etait pas super precis comme calcul, mais j'ai depuis zappé le truc (laisssant une normalisation en bon vieux c++), il faudrait que je m'y remette
Marsh Posté le 26-03-2006 à 12:44:04
Salut,
J'essaies d'écrire le code le plus rapide pour normaliser un vecteur. Après pas mal de tatonnement je suis arrivé au code suivant :
void Normalize(TDVector3D &V)
{
__asm
{
mov eax,[ebp+0x8]
fld qword ptr [eax] // ST0=X X
fld ST // ST1=X ST0=X
fmul ST,ST // ST1=X ST0=X²
fld qword ptr [eax+0x8] // ST2=X ST1=X² ST0=Y Y
fld ST // ST3=X ST2=X² ST1=Y ST0=Y
fmul ST,ST // ST3=X ST2=X² ST1=Y ST0=Y²
faddp ST(2),ST // ST2=X ST1=X²+Y² ST0=Y
fld qword ptr [eax+0x10] // ST3=X ST2=X²+Y² ST1=Y ST0=Z Z
fld ST // ST4=X ST3=X²+Y² ST2=Y ST1=Z ST0=Z
fmul ST,ST // ST4=X ST3=X²+Y² ST2=Y ST1=Z ST0=Z²
faddp ST(3),ST // ST3=X ST2=X²+Y²+Z² ST1=Y ST0=Z
fxch ST(2) // ST3=X ST2=Z ST1=Y ST0=X²+Y²+Z²
fsqrt // ST3=X ST2=Z ST1=Y ST0=Sqrt(X²+Y²+Z²)
fld1 // ST4=X ST3=Z ST2=Y ST1=Sqrt(X²+Y²+Z²) ST0=1
fdivrp // ST3=X ST2=Z ST1=Y ST0=1/Sqrt(X²+Y²+Z²)=N
fmul ST(3),ST // ST3=X*N ST2=Z ST1=Y ST0=N
fmul ST(2),ST // ST3=X*N ST2=Z*N ST1=Y ST0=N
fmulp // ST2=X*N ST1=Z*N ST0=Y*N
fstp qword ptr [eax+0x8] // ST1=X*N ST0=Z*N Y
fstp qword ptr [eax+0x10] // ST0=X*N Z
fstp qword ptr [eax] // vide X
fwait
}
}
ou j'ai défini le type vecteur :
typedef struct
{
double X, Y, Z;
} TDVector3D;
Cela marche super bien, mais je voudrais encore plus rapide !!!
Je suis tombé que le bout de code suivant :
float fast_invSqrt(float x)
{
float xhalf = 0.5f*x;
int i = *(int*)&x; // get bits for floating value
i = 0x5f3759df - (i>>1); // give initial guess y0
x = *(float*)&i; // convert bits back to float
x *= 1.5f - xhalf*x*x; // newton step, repeating this step increases accuracy
x *= 1.5f - xhalf*x*x;
return x;
}
qui calcule une approximation rapide de 1/Sqrt(x). J'ai voulu traduire ce code en assembleur pour l'intégrer dans le mien mais je n'y suis pas arrivé. Je ne sais pas comment traduire tous ces transtypages ....
J'ai aussi voulu mettre juste avant l'appel de fsqrt un test de nullité et d'égalité à 1. Le code suivant refuse de fonctionner :
fldz
fcomp
fstsw AX
fwait
jnz @Fin
idem avec un fld1. @Fin est l'étiquette de sortie de la routine puisqu'il n'y a rien à faire si la norme du vecteur vaut 0 ou 1.
Est-ce qu'une bonne âme pourrait m'aider.
Cordialement,
Lionel