Page 1 of 1

Loop Design For Safe Parallel Processing with PB Example

Posted: Mon Dec 13, 2010 11:03 pm
by buddymatkona
Normal Program
.
. 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 **************************************

Re: Loop Design For Safe Parallel Processing with PB Example

Posted: Wed Aug 31, 2016 4:44 pm
by blueb
Dug up from the graveyard :mrgreen:
buddymatkona's code appears to work well...

Perhaps too well??

I modified it to get 32 threads (16 core/32 thread machine)
and I get...

Code: Select all

-----Starting Thread Number: 1
08 31 2016 08:40 Thread Number: 1 is ending. Thread Execution Time(msec)= 14
-----Starting Thread Number: 2
08 31 2016 08:40 Thread Number: 2 is ending. Thread Execution Time(msec)= 13
-----Starting Thread Number: 3
08 31 2016 08:40 Thread Number: 3 is ending. Thread Execution Time(msec)= 11
-----Starting Thread Number: 4
08 31 2016 08:40 Thread Number: 4 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 5
08 31 2016 08:40 Thread Number: 5 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 6
08 31 2016 08:40 Thread Number: 6 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 7
08 31 2016 08:40 Thread Number: 7 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 8
08 31 2016 08:40 Thread Number: 8 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 9
08 31 2016 08:40 Thread Number: 9 is ending. Thread Execution Time(msec)= 5
-----Starting Thread Number: 10
08 31 2016 08:40 Thread Number: 10 is ending. Thread Execution Time(msec)= 5
-----Starting Thread Number: 11
08 31 2016 08:40 Thread Number: 11 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 12
08 31 2016 08:40 Thread Number: 12 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 13
08 31 2016 08:40 Thread Number: 13 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 14
08 31 2016 08:40 Thread Number: 14 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 15
08 31 2016 08:40 Thread Number: 15 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 16
08 31 2016 08:40 Thread Number: 16 is ending. Thread Execution Time(msec)= 11
-----Starting Thread Number: 17
08 31 2016 08:40 Thread Number: 17 is ending. Thread Execution Time(msec)= 9
-----Starting Thread Number: 18
08 31 2016 08:40 Thread Number: 18 is ending. Thread Execution Time(msec)= 8
-----Starting Thread Number: 19
08 31 2016 08:40 Thread Number: 19 is ending. Thread Execution Time(msec)= 7
-----Starting Thread Number: 20
08 31 2016 08:40 Thread Number: 20 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 21
08 31 2016 08:40 Thread Number: 21 is ending. Thread Execution Time(msec)= 7
-----Starting Thread Number: 22
08 31 2016 08:40 Thread Number: 22 is ending. Thread Execution Time(msec)= 7
-----Starting Thread Number: 23
08 31 2016 08:40 Thread Number: 23 is ending. Thread Execution Time(msec)= 21
-----Starting Thread Number: 24
08 31 2016 08:40 Thread Number: 24 is ending. Thread Execution Time(msec)= 7
-----Starting Thread Number: 25
08 31 2016 08:40 Thread Number: 25 is ending. Thread Execution Time(msec)= 7
-----Starting Thread Number: 26
08 31 2016 08:40 Thread Number: 26 is ending. Thread Execution Time(msec)= 7
-----Starting Thread Number: 27
08 31 2016 08:40 Thread Number: 27 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 28
08 31 2016 08:40 Thread Number: 28 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 29
08 31 2016 08:40 Thread Number: 29 is ending. Thread Execution Time(msec)= 5
-----Starting Thread Number: 30
08 31 2016 08:40 Thread Number: 30 is ending. Thread Execution Time(msec)= 11
-----Starting Thread Number: 31
08 31 2016 08:40 Thread Number: 31 is ending. Thread Execution Time(msec)= 6
-----Starting Thread Number: 32
08 31 2016 08:40 Thread Number: 32 is ending. Thread Execution Time(msec)= 12
32 threads finished.                          Total  Time(msec)= 263
 Press ENTER To quit ThreadTest.
Can anyone test this?
Any comments?