Optimisation des accès mémoires

Programmation d'applications complexes
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Optimisation des accès mémoires

Message 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
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Optimisation des accès mémoires

Message 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.

@+
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Optimisation des accès mémoires

Message par djes »

Oui, bizarrement, le AllocateMemory() ne renvoie pas un espace mémoire aligné sur 16 octets...
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Optimisation des accès mémoires

Message 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
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Optimisation des accès mémoires

Message 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
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Optimisation des accès mémoires

Message 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 :)
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Optimisation des accès mémoires

Message 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:
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Optimisation des accès mémoires

Message 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:
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Optimisation des accès mémoires

Message par G-Rom »

bah oui , je suis bête... :mrgreen:
j'ai calculer trop vite ^^

Merci des tuyaux
Atomo
Messages : 207
Inscription : lun. 17/sept./2007 12:27

Re: Optimisation des accès mémoires

Message 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))
Dernière modification par Atomo le sam. 30/oct./2010 12:12, modifié 4 fois.
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Optimisation des accès mémoires

Message par G-Rom »

8O
Super code atomo , qui plus est portable ! couplé à des instruction SSE4 ca dois déchiré à mort :mrgreen:
Atomo
Messages : 207
Inscription : lun. 17/sept./2007 12:27

Re: Optimisation des accès mémoires

Message 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:
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Optimisation des accès mémoires

Message par G-Rom »

Merci de la correction Atomo , je vais me gavé avec ca ! :mrgreen:
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Re: Optimisation des accès mémoires

Message 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 :)
Répondre