Page 1 sur 1

multiple coeurs

Publié : ven. 28/févr./2014 11:38
par zazoux
Bonjour,

j'aimerais savoir si en Purebasic il est possible de gerer le nombre de cooeurs du processeur, ou au moins établir à participer au déroulement du programme tous les coeurs disponibles. Et si oui, dans quel menu ça se passe ?

Merci en avance pour vos éclaircissements

Re: multiple coeurs

Publié : ven. 28/févr./2014 12:15
par Mesa
J'avais trouvé ça quelque part dans le forum anglais, il y a 3 ou 4 ans.

Code : Tout sélectionner

;ATTENTION METTRE MULTITHREAD DANS LE COMPILATEUR

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


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

M.

Re: multiple coeurs

Publié : ven. 28/févr./2014 13:57
par djes
Un petit commentaire vite fait, car ça m'arrache les yeux :

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
Il y a là matière à une foule d'optims qui devraient être naturelles pour le codeur, en plus il y a des erreurs. Déjà, quand il y a des *4, on sait qu'il va y avoir de l'optim. On pourrait faire la multiplication dans le "for" une fois pour toutes. En plus le buffer est dépassé (y'a clairement un bug!!!), alors il met une horrible condition répétée à chaque itération!!!!!!! Si on sait la taille de son buffer, il n'y a pas à mettre de condition, c'est inutile et coûteux en terme de performances. Enfin voilà quoi, un peu de qualité dans le code SVP... :?