Page 2 sur 2
Re: Optimisation des accès mémoires
Publié : ven. 29/oct./2010 23:21
par djes
Code : Tout sélectionner
Structure vector4
x.f
y.f
z.f
w.f
EndStructure
*V1.vector4 = ?vector1
*V2.vector4 = ?vector2
Vres.vector4
*V1\x = 50
*V1\y = 100
*V1\z = 245
*V1\w = 135
*V2\x = 100
*V2\y = 100
*V2\z = 100
*V2\w = 100
CallDebugger
!movaps xmm0, [v1]
!addps xmm0, [v2]
!movaps [v1], xmm0
Debug *V1\x
Debug *V1\y
Debug *V1\z
Debug *V1\w
End
; movaps xmm0,adresse-de-v1 ;xmm0=v1.w | v1.z | v1.y | v1.x
; addps xmm0,adresse-de-v2 ;xmm0=v1.w+v2.w | v1.z+v2.z | v1.y+v2.y | v1.x+v2.x
; movaps adresse-du-vec_res,xmm0
!section '.data' align 16
!v1:
vector1:
!dd 0,0,0,0
!v2:
vector2:
!dd 0,0,0,0
Désolé pour le jonglage à cause de l'alignement nécessaire des données
Re: Optimisation des accès mémoires
Publié : ven. 29/oct./2010 23:46
par G-Rom
c'est chiant cette histoire d'alignement , dommage qu'on ne peut pas le faire directos sur la structure.
Merci de la lecon d'asm.
@+
Re: Optimisation des accès mémoires
Publié : ven. 29/oct./2010 23:52
par djes
Oui, bizarrement, le AllocateMemory() ne renvoie pas un espace mémoire aligné sur 16 octets...
Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 0:10
par djes
Voici une version un peu bourrin (àla C) avec AllocateMemory() (32 bits seulement, vu le masque du And)
Code : Tout sélectionner
Structure vector4
x.f
y.f
z.f
w.f
EndStructure
*V1.vector4 = AllocateMemory(SizeOf(vector4) + 16)
*V1 = *V1 & $FFFFFFF0
*V2.vector4 = AllocateMemory(SizeOf(vector4) + 16)
*V2 = *V2 & $FFFFFFF0
Vres.vector4
*V1\x = 50
*V1\y = 100
*V1\z = 245
*V1\w = 135
*V2\x = 100
*V2\y = 100
*V2\z = 100
*V2\w = 100
;CallDebugger
!mov eax, dword[p_V1]
!mov ebx, dword[p_V2]
!movaps xmm0, [eax]
!addps xmm0, [ebx]
!movaps [eax], xmm0
Debug *V1\x
Debug *V1\y
Debug *V1\z
Debug *V1\w
End
; movaps xmm0,adresse-de-v1 ;xmm0=v1.w | v1.z | v1.y | v1.x
; addps xmm0,adresse-de-v2 ;xmm0=v1.w+v2.w | v1.z+v2.z | v1.y+v2.y | v1.x+v2.x
; movaps adresse-du-vec_res,xmm0
Là, un code un peu plus respectueux
http://www.purebasic.fr/english/viewtop ... 63#p335963
Et là, encore mieux!
http://www.purebasic.fr/english/viewtop ... 27&start=0
Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 0:24
par G-Rom
en écrivant comme ca ca passe :
Code : Tout sélectionner
*V1.vector4 = AllocateMemory(SizeOf(vector4)) & $FFFFFFF0
*V2.vector4 = AllocateMemory(SizeOf(vector4)) & $FFFFFFF0
Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 0:28
par djes
Oulà, n'oublie pas d'ajouter 16 octets à ton allocation, sinon c'est le débordement assuré. Et sur un système 64 bits, le masque serait plutôt $FFFFFFFFFFFFFFF0 (sinon, tu vas poker directement 4Go avant

). C'est pour ça que la petite routine que j'ai liée est mieux, elle efface le dernier quartet en partant de la fin

Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 0:31
par G-Rom
c'est pas 4 x 32bits xmm. ? ou par sécurité peut être ??
je viens de faire un petit bench de vitesse , SSE explose un code standard.

Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 0:42
par djes
Par sécurité (4*4 octets=16 octets). Et n'oublie pas que tu as 8 (voire 16) registres xmm, tu fais

Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 0:51
par G-Rom
bah oui , je suis bête...
j'ai calculer trop vite ^^
Merci des tuyaux
Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 10:23
par Atomo
Je ne sais pas si ça convient à ton cas mais en faisant du multi-thread, j'obtiens de bonnes performances.
1 thread = 343ms
2 thread = 188ms
Je n'ai pas pu tester avec plus de thread, j'ai qu'un double coeurs, mais le code qui suit s'adapte automatiquement.
A compiler en mode ThreadSafe !
Fichier include :Code : Tout sélectionner
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows ;{
Global Total_CPU = Val(GetEnvironmentVariable("NUMBER_OF_PROCESSORS"))-1
;}
CompilerCase #PB_OS_Linux ;{
grep = RunProgram("grep","-c processor /proc/cpuinfo","./",#PB_Program_Open|#PB_Program_Read)
If grep
Sortie$ + ReadProgramString(grep) + Chr(13)
Global Total_CPU = Val(Sortie$)-1
EndIf
;}
CompilerEndSelect
Global wait_thread = CreateSemaphore()
Global mutex = CreateMutex()
Macro P_For(variable, start)
For variable=start+core
EndMacro
Macro P_Next(variable)
variable+Total_CPU
Next variable
EndMacro
Structure ParallelFor
thread.i
core.i
parent.i
wait.b
return_procedure.i
EndStructure
Global NewList Working_Threads.ParallelFor()
Prototype ProtoLoop(core, parametre)
Global Loop.ProtoLoop
Procedure WorkerThread(parametre)
thread_id = Working_Threads()\thread
core = Working_Threads()\core
wait = Working_Threads()\wait
SignalSemaphore(wait_thread)
Resultat = Loop(core, parametre)
;Fin du thread
LockMutex(mutex)
ForEach Working_Threads()
If Working_Threads()\thread = thread_id
If wait = #False
;efface le thread
DeleteElement(Working_Threads())
Break
Else
;procedure return
If Working_Threads()\return_procedure = 0
Working_Threads()\return_procedure = Resultat
Break
EndIf
EndIf
EndIf
Next
UnlockMutex(mutex)
EndProcedure
Procedure ParallelFor(id, wait.b, *procedure_ptr, parametre)
Loop.ProtoLoop = *procedure_ptr
LockMutex(mutex)
For x=0 To Total_CPU
AddElement(Working_Threads())
Working_Threads()\core = x
Working_Threads()\thread = CreateThread(@WorkerThread(), parametre)
Working_Threads()\parent = id
Working_Threads()\wait = wait
If x=0
first_thread = Working_Threads()\thread
EndIf
WaitSemaphore(wait_thread)
Next x
UnlockMutex(mutex)
If wait = #True
For x=0 To Total_CPU
WaitThread(first_thread+x)
Next x
;procedurereturn et efface les threads
LockMutex(mutex)
ForEach Working_Threads()
If Working_Threads()\parent = id
Resultat = Working_Threads()\return_procedure
If Resultat
return_procedure = Resultat
EndIf
DeleteElement(Working_Threads())
EndIf
Next
UnlockMutex(mutex)
ProcedureReturn return_procedure
Else
ProcedureReturn id
EndIf
EndProcedure
Procedure IsParallelFor(id)
LockMutex(mutex)
ForEach Working_Threads()
If Working_Threads()\parent = id
ProcedureReturn 1
EndIf
Next
UnlockMutex(mutex)
EndProcedure
Procedure KillParallelFor(id)
LockMutex(mutex)
ForEach Working_Threads()
If Working_Threads()\parent = id
KillThread(Working_Threads()\thread)
DeleteElement(Working_Threads())
EndIf
Next
UnlockMutex(mutex)
EndProcedure
Procedure PauseParallelFor(id)
LockMutex(mutex)
ForEach Working_Threads()
If Working_Threads()\parent = id
PauseThread(Working_Threads()\thread)
EndIf
Next
UnlockMutex(mutex)
EndProcedure
Procedure ResumeParallelFor(id)
LockMutex(mutex)
ForEach Working_Threads()
If Working_Threads()\parent = id
ResumeThread(Working_Threads()\thread)
EndIf
Next
UnlockMutex(mutex)
EndProcedure
Exemple :
Code : Tout sélectionner
IncludeFile "Multicore.pbi"
Structure zbuffer
*depth_buffer.i
w.l
h.l
EndStructure
ProcedureDLL.i zbuffer(w.l, h.l, mFar.f)
*zb.zbuffer = AllocateMemory(SizeOf(zbuffer))
*zb\depth_buffer = AllocateMemory( w * h * 4 )
*zb\w = w
*zb\h = h
FillMemory(*zb\depth_buffer , MemorySize(*zb\depth_buffer), mFar, #PB_Long)
ProcedureReturn *zb
EndProcedure
ProcedureDLL zbuffer_write(*zb.zbuffer)
For i = 0 To 50
For y = 0 To 480-1
For x = 0 To 640
If x=>0 And x < *zb\w And y=>0 And y < *zb\h
PokeF(*zb\depth_buffer + (x*4) + *zb\w * (y*4), Random(1500))
EndIf
Next
Next
Next
EndProcedure
ProcedureDLL zbuffer_write_multi(core, *zb.zbuffer)
P_For(i, 0) To 50
For y=0 To 480-1
For x=0 To 640
If x=>0 And x < *zb\w And y=>0 And y < *zb\h
PokeF(*zb\depth_buffer + (x*4) + *zb\w * (y*4), Random(1500))
EndIf
Next x
Next y
P_Next(i)
EndProcedure
Buffer = zbuffer(640,480,1500)
;Single thread
A = ElapsedMilliseconds()
zbuffer_write(Buffer)
B = ElapsedMilliseconds()
WriteTime = B-A
MessageRequester("Single thread", Str(WriteTime))
;Multi thread
A = ElapsedMilliseconds()
ParallelFor(0, #True, @zbuffer_write_multi(), Buffer)
B = ElapsedMilliseconds()
WriteTime = B-A
MessageRequester("Multi thread", Str(WriteTime))
Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 11:11
par G-Rom
Super code atomo , qui plus est portable ! couplé à des instruction SSE4 ca dois déchiré à mort

Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 11:54
par Atomo
Mon code est erroné
Il faut compiler en mode ThreadSafe puis remplacer la procedure :
Code : Tout sélectionner
ProcedureDLL zbuffer_write_multi(core, *zb.zbuffer)
P_For(i, 0) To 50
P_For(y, 0) To 480-1
P_For(x, 0) To 640
If x=>0 And x < *zb\w And y=>0 And y < *zb\h
PokeF(*zb\depth_buffer + (x*4) + *zb\w * (y*4), Random(1500))
EndIf
P_Next(x)
P_Next(y)
P_Next(i)
EndProcedure
Par celle-ci:
Code : Tout sélectionner
ProcedureDLL zbuffer_write_multi(core, *zb.zbuffer)
P_For(i, 0) To 50
For y=0 To 480-1
For x=0 To 640
If x=>0 And x < *zb\w And y=>0 And y < *zb\h
PokeF(*zb\depth_buffer + (x*4) + *zb\w * (y*4), Random(1500))
EndIf
Next x
Next y
P_Next(i)
EndProcedure
Quand il y a plusieurs boucles imbriquées, il faut en paralléliser qu'une seul (n'importe laquelle fera l'affaire) et non pas toutes comme dans mon exemple précédant.
C'est moins rapide qu'avant mais au moins ça fonctionne puis c'est toujours 2X plus rapide

Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 12:49
par G-Rom
Merci de la correction Atomo , je vais me gavé avec ca !

Re: Optimisation des accès mémoires
Publié : sam. 30/oct./2010 13:00
par Backup
@Atomo :
Mieux vaut utiliser le bouton "Editer" en bas de ton message
pour modifier ton code
plutot que de donner des portions de code
a remplacer, comme c'est courrant de le faire dans les Forums PHP
je trouve que ça complique pour rien
tu edite ton code , tu fais ta modif
et avec un message , tu signale que tu as modifé ton code
cela évite une succession de codes , seul le premier code est apparent
comme cela
