.
. code as usual until you get to a cpu-intensive loop
.
BIGLOOP ...Break it up and do Part1, Part2, ...PartN in parallel
let the loop finish
. then resume normal sequential processing
.
. code as usual
.
End
Do you have a multicore processor with some idle CPUs? Whenever you have a loop doing something repetitive to a lot of data, you may want to break up the work. A quad core with hyperthreading enabled can do such processing in about 1/8 the time it takes a single CPU.
To keep parallel processing simple and avoid race conditions, break loops up into threaded segments and give threads their own Input/Output blocks of global memory. A PureBasic console example showing one way to segment a loop and run segments in parallel is shown below. You can experiment with different workloads and different numbers of threads by changing control parameters in the first line of code. Use that idle time!
Code: Select all
#SimWorkLoop = 40000 : N = 1000 : NThreads = 3 ; Loop Controls for adding workload, count for the loop to be segmented, number of segments (threads)
;Compile with Debugger - NThreads loop segments will be done sequentially in one thread
;Compile without Debugger - Nthreads loop segments will be done in parallel threads (NThreads = number of near-idle CPUs should be optimum)
Global Dim RecIn(N + 1) ;Define thread input data in MAIN before launching the threads
Global Dim RecOut(N + 1) ;Thread output area - use any shared resources such as File I/O in MAIN
Global Dim Start(NThreads + 1) ; Start index within arrays RecIN and RecOut for each thread
Global Dim Stop(NThreads + 1) ; Start(ThreadNumber) to Stop(ThreadNumber) defines each thread's share of the data
Dim AllThreads(NThreads + 1) ; OS assigned ThreadIDs
Macro ThreadLoop(ProcName)
CompilerIf #PB_Compiler_Debugger = 0
For ICreate = 1 To NThreads : AllThreads(ICreate) = CreateThread(@ProcName(), ICreate) : Delay(50) : Next ICreate ;Do threads in parallel
For iwait = 1 To NThreads : WaitThread(AllThreads(iwait)) : Delay(50) : Next iwait ;Without a delay between thread calls, PureBasic will crash.
;PrintN (" PB_Compiler_Debugger = " + Str(#PB_Compiler_Debugger))
CompilerElse
For I = 1 To NThreads
ProcName(i) ; The loop segments will be done in sequence with ordinary procedure calls if #PB_Compiler_Debugger <> 0
;PrintN (" PB_Compiler_Debugger = " + Str(#PB_Compiler_Debugger))
Next i
CompilerEndIf
EndMacro
Macro SegmentLoop(LoopCount, NumberSegments, StartArray, StopArray)
; Divide the loop count into NumberSegments of Start-Stop indices for parallel processing
PerThread = (LoopCount+NumberSegments/2)/NumberSegments ; thread size portion of the original loop count
For I = 1 To NumberSegments
StartArray(I) = PerThread * (I - 1) + 1
StopArray(I) = PerThread * I
Next I
StopArray(NumberSegments) = LoopCount
EndMacro
Procedure DoTheWork(*ThreadNumber)
StartTime.i = ElapsedMilliseconds()
tn.i = *ThreadNumber ; shorter name :)
PrintN("-----Starting Thread Number: "+Str(tn))
For IWork = 1 To #SimWorkLoop ; simulate work
InputSum.i = 0
For MyLoopPart = Start(tn) To Stop(tn)
InputSum = InputSum + RecIn(MyLoopPart) ; dummy input data
RecOut(MyLoopPart) = tn * 1000 + MyLoopPart ; thread specific return data
Next MyLoopPart
Next IWork
WorkTime.i = ElapsedMilliseconds() - StartTime
WorkStop.s = FormatDate("%mm %dd %yyyy %hh:%ii", Date())
PrintN(WorkStop +" Thread Number: " + Str(tn) + " is ending. Thread Execution Time(msec)= " + Str(WorkTime))
EndProcedure
;************************ THREADTEST **************************************
SegmentLoop(N, NThreads, Start, Stop) ; Macro to Divide a 1-N loop into Start-Stop segments
For I = 1 To N : RecIn(I) = I : Next I ; Simulate input data
OpenConsole()
StartTime.i = ElapsedMilliseconds()
ThreadLoop(DoTheWork) ; Macro to Parallel Process loop segments when you compile without Debugger
WorkTime.i = ElapsedMilliseconds() - StartTime
PrintN(Str(NThreads) + " threads finished. Total Time(msec)= " + Str(WorkTime) )
PrintN (" Press ENTER To quit ThreadTest.")
Input()
End ;************************ THREADTEST **************************************