- supported : win / linux ( because there's no yet a Mac specific code for the following function GetCPUcount() )
- tested on a dual core machine.
I didn't keep any macros of the previous version.
This one is easier to use in my opinion.

Code: Select all
EnableExplicit
Global ParallelCPUCount
Global ParallelMutex
Global ParallelStart
Global ParallelStarted
Global ParallelRunning
Global *ParallelValue
Global *ParallelFunc
Global *ParallelCustomData
Threaded ParallelLocked.b ; MUST NOT be global, so every thread gets its own copy of this variable!
Procedure GetCPUCount()
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
Protected Count, i, ProcessMask, SystemMask
If GetProcessAffinityMask_(GetCurrentProcess_(), @ProcessMask, @SystemMask)
For i=0 To 31
If ProcessMask & (1<<i)
Count+1
EndIf
Next
EndIf
If Count=0
ProcedureReturn 1
Else
ProcedureReturn Count
EndIf
CompilerCase #PB_OS_Linux
;
; Code by remi_meier
;
Protected.i file, count, dir
Protected.s s
If FileSize("/proc/cpuinfo")<>-1
file=ReadFile(#PB_Any, "/proc/cpuinfo")
If IsFile(file)
count=0
While Not Eof(file)
s.s=ReadString(file)
If Left(s, 9)="processor"
count+1
EndIf
Wend
CloseFile(file)
EndIf
Else
dir=ExamineDirectory(#PB_Any, "/proc/acpi/processor", "")
count=0
If IsDirectory(dir)
While NextDirectoryEntry(dir)
If Left(DirectoryEntryName(dir), 3)="CPU"
count+1
EndIf
Wend
FinishDirectory(dir)
EndIf
EndIf
ProcedureReturn count
CompilerEndSelect
EndProcedure
Procedure ParallelWorkerThread(dummy)
Protected *Function, *CurrentValue, *CustomData
Repeat
WaitSemaphore(ParallelStart) ; wait for "weakup" - semaphore
WaitSemaphore(ParallelRunning) ; this one should be immediately available (allows to later know when the work is complete)
*CustomData=*ParallelCustomData
*Function=*ParallelFunc
*CurrentValue=*ParallelValue ; copy the values locally
SignalSemaphore(ParallelStarted) ; signal the "startup-complete" semaphore so the next thread can be started
CallFunctionFast(*Function, *CurrentValue, *CustomData) ; execute function
SignalSemaphore(ParallelRunning) ; signal completion of the job
ForEver
EndProcedure
Procedure CallParallel(*Function, *CurrentValue, *CustomData=#Null) ; Call a function in parallel (only if multiple processors are available)
If ParallelCPUCount>1 And (ParallelLocked Or TryLockMutex(ParallelMutex))
ParallelLocked=1
; specify the parallel function (with its custom data if necessary)
*ParallelFunc=*Function
*ParallelCustomData=*CustomData
; release one thread at a time so they can access the "CurrentValue"
; only the thread startup is serialized. the Loop() is run in parallel
*ParallelValue=*CurrentValue
SignalSemaphore(ParallelStart)
WaitSemaphore(ParallelStarted)
Else
CallFunctionFast(*Function, *CurrentValue, *CustomData)
EndIf
EndProcedure
Procedure FinishParallel() ; Waits for all running jobs to complete
Define i
If ParallelLocked
; wait for all worker threads to complete their work
For i=1 To ParallelCPUCount
WaitSemaphore(ParallelRunning)
Next
; reset the running count semaphore back to the start value
For i=1 To ParallelCPUCount
SignalSemaphore(ParallelRunning);, ProcessorCount)
Next
ParallelLocked=0
UnlockMutex(ParallelMutex)
EndIf
EndProcedure
Procedure InitParallel(MinThreads=1) ; Initializes the parallel code ('MinThreads' >= your actual processor/core count)
ParallelCPUCount=GetCPUCount()
If ParallelCPUCount<MinThreads
ParallelCPUCount=MinThreads
EndIf
If ParallelCPUCount>1
ParallelMutex=CreateMutex()
ParallelStart=CreateSemaphore(0) ;max=1
ParallelStarted=CreateSemaphore(0) ;max=1
ParallelRunning=CreateSemaphore(ParallelCPUCount) ;max= ProcessorCount
Protected i
For i=1 To ParallelCPUCount
CreateThread(@ParallelWorkerThread(), 0)
Next
CompilerIf #PB_Compiler_Thread=0
MessageRequester("Init Parallel", "Initialization failed because your executable is not compiled in ThreadSafe mode.")
End
CompilerEndIf
EndIf
EndProcedure
DisableExplicit
; ********************
; Example
; ********************
InitParallel()
Define maxX=1024
Define maxY=1024
Define img=CreateImage(#PB_Any, maxX, maxY, 32)
Define x, y
Procedure DrawingPlots(y, img)
StartDrawing(ImageOutput(img))
Define x
For x=0 To ImageWidth(img)-1
Define xx, yy, zz
xx=x%256
xx/#PI
xx=Pow(Sin(xx)*$F, 7.3)
yy=y%512
yy/#PI
yy=Pow(Cos(xx/3+yy)*$F, xx)
zz=(xx ! yy) & $FF
Plot(x, y, RGB(xx, yy, zz))
Next
StopDrawing()
EndProcedure
;/// Linear Coding
Delay(100)
startTime=ElapsedMilliseconds()
For y=0 To ImageHeight(img)-1
DrawingPlots(y, img)
Next
duration1=ElapsedMilliseconds()-startTime
;/// Parallel Coding
Delay(100)
startTime=ElapsedMilliseconds()
For y=0 To ImageHeight(img)-1
CallParallel(@DrawingPlots(), y, img)
Next
FinishParallel()
duration2=ElapsedMilliseconds()-startTime
MessageRequester("Terminated ", "Time: "+Str(duration1)+#LF$+"Time (optimized, CPU count="+Str(GetCPUCount())+"): "+Str(duration2)+#LF$)
OpenWindow(0, 0, 0, 800, 600, "Parallel Coding", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
ScrollAreaGadget(1, 0, 0, 800, 600, maxX, maxY)
ImageGadget(0, 0, 0, 0, 0, ImageID(img))
CloseGadgetList()
Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow