Je crois que le docteur a lu un peu en diagonale
J'avais rien à faire et y fait pas beau, alors j'ai essayé cette méthode avec les instructions SSE2, pour calculer tout un paquet de racines à la fois, mais je n'obtiens rien de mieux qu'avec l'instruction SSE "sqrtps" toute simple toute bête:
Code : Tout sélectionner
Procedure.f diffmax(*src1.float, *src2.float, longueur)
max.f = 0.0
While longueur
diff.f = Abs(*src1\f - *src2\f)
If diff > max
max = diff
EndIf
*src1 + 4
*src2 + 4
longueur - 1
Wend
ProcedureReturn max
EndProcedure
Procedure SQR_FPU(source, destination, longueur)
!mov esi , [esp]
!mov edi , [esp+4]
!mov eax , [esp+8]
!mov ebx , 4
!test eax , eax
!jz .Fin
!.Bouclette:
!fld dword[esi]
!fsqrt
!fstp dword[edi]
!add esi , ebx
!add edi , ebx
!dec eax
!jnz .Bouclette
!.Fin:
EndProcedure
; Attention: le addresses source et destination doivent être alignées
; sur 16 octets, et la longueur(cad le nombre de flottants 32bits à traiter)
; doit être multiple de 4
Procedure SQR_SSE_simple(source, destination, longueur)
!mov esi , [esp]
!mov edi , [esp+4]
!mov eax , [esp+8]
!mov ebx , 16
!shr eax , 2
!jz .Fin
!.Bouclette:
!movaps xmm0 , [esi]
!sqrtps xmm1 , xmm0
!movaps [edi] , xmm1
!add esi , ebx
!add edi , ebx
!dec eax
!jnz .Bouclette
!.Fin:
EndProcedure
; Attention: le addresses source et destination doivent être alignées
; sur 16 octets, et la longueur(cad le nombre de flottants 32bits à traiter)
; doit être multiple de 4
Procedure SQR_SSE2_plus_truc(source, destination, longueur)
!mov eax , 0.5
!push eax
!push eax
!push eax
!push eax
!movups xmm1 , [esp]
!_0p5 equ xmm1
!mov eax , 5F3759DFh
!mov [esp] , eax
!mov [esp+4] , eax
!mov [esp+8] , eax
!mov [esp+12] , eax
!movups xmm2 , [esp]
!magic equ xmm2
!mov eax , 1.5
!mov [esp] , eax
!mov [esp+4] , eax
!mov [esp+8] , eax
!mov [esp+12] , eax
!movups xmm3 , [esp]
!_1p5 equ xmm3
!mov eax , 1.0
!mov [esp] , eax
!mov [esp+4] , eax
!mov [esp+8] , eax
!mov [esp+12] , eax
!movups xmm4 , [esp]
!_1p0 equ xmm4
!add esp , 16
!mov esi , [esp]
!mov edi , [esp+4]
!mov eax , [esp+8]
!mov ebx , 16
!shr eax , 2
!jz .Fin
!.Bouclette:
!movaps xmm0 , [esi]
!movaps xmm5 , _0p5
!mulps xmm5 , xmm0
!psrld xmm0 , 1
!movaps xmm6 , magic
!psubd xmm6 , xmm0
!mulps xmm5 , xmm6
!mulps xmm5 , xmm6
!movaps xmm7 , _1p5
!subps xmm7 , xmm5
!mulps xmm7 , xmm6
!movaps xmm0 , _1p0
!divps xmm0 , xmm7
!movaps [edi] , xmm0
!add esi , ebx
!add edi , ebx
!dec eax
!jnz .Bouclette
!.Fin:
EndProcedure
Structure AlignedMemBlock
AlignedAddr.l
AllocAddr.l
EndStructure
Procedure.l AllocateAlignedMemBlock(*BlockInfo.AlignedMemBlock, size, Alignment)
r = AllocateMemory(size + alignment)
If r
*BlockInfo\allocaddr = r
*BlockInfo\alignedaddr = r + alignment - r % alignment
Else
*BlockInfo\allocaddr = 0
*BlockInfo\alignedaddr = 0
EndIf
ProcedureReturn r
EndProcedure
DefType.AlignedMemBlock Source, DestFPU, DestSSE_simple, DestSSE2_plus_truc
#TestLen = 1000000
AllocateAlignedMemBlock(@Source, #TestLen * 16, 16)
AllocateAlignedMemBlock(@DestFPU, #TestLen * 16, 16)
AllocateAlignedMemBlock(@DestSSE_simple, #TestLen * 16, 16)
AllocateAlignedMemBlock(@DestSSE2_plus_truc, #TestLen * 16, 16)
*initieur.float = Source\alignedaddr
For i = 1 To #testlen * 4
*initieur\f = Random(10000)
*initieur + 4
Next
For i = 1 To 30
depchrono = ElapsedMilliseconds()
SQR_FPU(source\alignedaddr, DestFPU\alignedaddr, #testlen * 4)
finchrono = ElapsedMilliseconds()
chronoFPU + finchrono - depchrono
depchrono = ElapsedMilliseconds()
SQR_SSE_simple(source\alignedaddr, DestSSE_simple\alignedaddr, #testlen * 4)
finchrono = ElapsedMilliseconds()
chronoSSE_simple + finchrono - depchrono
depchrono = ElapsedMilliseconds()
SQR_SSE_simple(source\alignedaddr, DestSSE2_plus_truc\alignedaddr, #testlen * 4)
finchrono = ElapsedMilliseconds()
chronoSSE2_plus_truc + finchrono - depchrono
Next
MessageRequester("", "FPU: " + Str(chronoFPU) + #CR$ + "SSE simple: " + Str(chronoSSE_simple) + #CR$ + "SSE2 + truc: " + Str(chronoSSE2_plus_truc))
MessageRequester("différence max FPU-SSE simple", StrF(diffmax(DestFPU\alignedaddr, DestSSE_simple\alignedaddr, #testlen * 4)))
MessageRequester("différence max FPU-SSE2 + truc", StrF(diffmax(DestFPU\alignedaddr, DestSSE2_plus_truc\alignedaddr, #testlen * 4)))
Pour calculer un paquet d'inverses de la racine carrée, l'écart FPU-SSE se creuse de manière intéressante(tout simplement parceque SSE a une instruction exprès pour ça), mais je n'arrive toujours pas à tirer parti de la "méthode magique":
Code : Tout sélectionner
Procedure.f diffmax(*src1.float, *src2.float, longueur)
max.f = 0.0
While longueur
diff.f = Abs(*src1\f - *src2\f)
If diff > max
max = diff
EndIf
*src1 + 4
*src2 + 4
longueur - 1
Wend
ProcedureReturn max
EndProcedure
Procedure INVSQR_FPU(source, destination, longueur)
!mov esi , [esp]
!mov edi , [esp+4]
!mov eax , [esp+8]
!mov ebx , 4
!test eax , eax
!jz .Fin
!.Bouclette:
!fld1
!fld dword[esi]
!fsqrt
!fdivp st1,st0
!fstp dword[edi]
!add esi , ebx
!add edi , ebx
!dec eax
!jnz .Bouclette
!.Fin:
EndProcedure
; Attention: le addresses source et destination doivent être alignées
; sur 16 octets, et la longueur(cad le nombre de flottants 32bits à traiter)
; doit être multiple de 4
Procedure INVSQR_SSE_simple(source, destination, longueur)
!mov esi , [esp]
!mov edi , [esp+4]
!mov eax , [esp+8]
!mov ebx , 16
!shr eax , 2
!jz .Fin
!.Bouclette:
!movaps xmm0 , [esi]
!rsqrtps xmm1 , xmm0
!movaps [edi] , xmm1
!add esi , ebx
!add edi , ebx
!dec eax
!jnz .Bouclette
!.Fin:
EndProcedure
; Attention: le addresses source et destination doivent être alignées
; sur 16 octets, et la longueur(cad le nombre de flottants 32bits à traiter)
; doit être multiple de 4
Procedure INVSQR_SSE2_plus_truc(source, destination, longueur)
!mov eax , 0.5
!push eax
!push eax
!push eax
!push eax
!movups xmm1 , [esp]
!_0p5 equ xmm1
!mov eax , 5F3759DFh
!mov [esp] , eax
!mov [esp+4] , eax
!mov [esp+8] , eax
!mov [esp+12] , eax
!movups xmm2 , [esp]
!magic equ xmm2
!mov eax , 1.5
!mov [esp] , eax
!mov [esp+4] , eax
!mov [esp+8] , eax
!mov [esp+12] , eax
!movups xmm3 , [esp]
!_1p5 equ xmm3
!add esp , 16
!mov esi , [esp]
!mov edi , [esp+4]
!mov eax , [esp+8]
!mov ebx , 16
!shr eax , 2
!jz .Fin
!.Bouclette:
!movaps xmm0 , [esi]
!movaps xmm5 , _0p5
!mulps xmm5 , xmm0
!psrld xmm0 , 1
!movaps xmm6 , magic
!psubd xmm6 , xmm0
!mulps xmm5 , xmm6
!mulps xmm5 , xmm6
!movaps xmm7 , _1p5
!subps xmm7 , xmm5
!mulps xmm7 , xmm6
!movaps [edi] , xmm7
!add esi , ebx
!add edi , ebx
!dec eax
!jnz .Bouclette
!.Fin:
EndProcedure
Structure AlignedMemBlock
AlignedAddr.l
AllocAddr.l
EndStructure
Procedure.l AllocateAlignedMemBlock(*BlockInfo.AlignedMemBlock, size, Alignment)
r = AllocateMemory(size + alignment)
If r
*BlockInfo\allocaddr = r
*BlockInfo\alignedaddr = r + alignment - r % alignment
Else
*BlockInfo\allocaddr = 0
*BlockInfo\alignedaddr = 0
EndIf
ProcedureReturn r
EndProcedure
DefType.AlignedMemBlock Source, DestFPU, DestSSE_simple, DestSSE2_plus_truc
#TestLen = 1000000
AllocateAlignedMemBlock(@Source, #TestLen * 16, 16)
AllocateAlignedMemBlock(@DestFPU, #TestLen * 16, 16)
AllocateAlignedMemBlock(@DestSSE_simple, #TestLen * 16, 16)
AllocateAlignedMemBlock(@DestSSE2_plus_truc, #TestLen * 16, 16)
*initieur.float = Source\alignedaddr
For i = 1 To #testlen * 4
*initieur\f = Random(10000)
*initieur + 4
Next
For i = 1 To 30
depchrono = ElapsedMilliseconds()
INVSQR_FPU(source\alignedaddr, DestFPU\alignedaddr, #testlen * 4)
finchrono = ElapsedMilliseconds()
chronoFPU + finchrono - depchrono
depchrono = ElapsedMilliseconds()
INVSQR_SSE_simple(source\alignedaddr, DestSSE_simple\alignedaddr, #testlen * 4)
finchrono = ElapsedMilliseconds()
chronoSSE_simple + finchrono - depchrono
depchrono = ElapsedMilliseconds()
INVSQR_SSE_simple(source\alignedaddr, DestSSE2_plus_truc\alignedaddr, #testlen * 4)
finchrono = ElapsedMilliseconds()
chronoSSE2_plus_truc + finchrono - depchrono
Next
MessageRequester("", "FPU: " + Str(chronoFPU) + #CR$ + "SSE simple: " + Str(chronoSSE_simple) + #CR$ + "SSE2 + truc: " + Str(chronoSSE2_plus_truc))
MessageRequester("différence max FPU-SSE simple", StrF(diffmax(DestFPU\alignedaddr, DestSSE_simple\alignedaddr, #testlen * 4)))
MessageRequester("différence max FPU-SSE2 + truc", StrF(diffmax(DestFPU\alignedaddr, DestSSE2_plus_truc\alignedaddr, #testlen * 4)))
Bon, c'était toujours plus rigolo que de regarder tomber les gouttes
Ce qui pourrait être intéressant - mais un poil plus couillu - ce serai d'intercaler des instructions CPU qui font autrechose en parallèle...
mais là j'ai peur que ça dépasse mes capacités de concentration!