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 :mrgreen: ). 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. :mrgreen:

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

Code : Tout sélectionner

!movaps xmm1, [eax + 16]
!movaps xmm2, [eax + 32]
...
:mrgreen:

Re: Optimisation des accès mémoires

Publié : sam. 30/oct./2010 0:51
par G-Rom
bah oui , je suis bête... :mrgreen:
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
8O
Super code atomo , qui plus est portable ! couplé à des instruction SSE4 ca dois déchiré à mort :mrgreen:

Re: Optimisation des accès mémoires

Publié : sam. 30/oct./2010 11:54
par Atomo
Mon code est erroné :oops:
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 :mrgreen:

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 ! :mrgreen:

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 :)