Parallel procedures

From Lazarus wiki
Revision as of 20:11, 12 December 2008 by Mattias2 (talk | contribs) (→‎Features)
Jump to navigationJump to search

Overview

This page describes how to run single procedures in parallel using the MTProcs unit. Parallel procedures and methods are often found in parallel algorithms and some languages provide built-in support for them (e.g. OpenMP in gcc). See here for the plans adding such language features to FPC. This page describes parallel procedures using the unit MTProcs, which simplifies running procedures in parallel.

First example

Here is a short example:

<Delphi> program Test;

{$mode objfpc}{$H+}

uses

 {$IFDEF UNIX}
 cthreads, cmem,
 {$ENDIF}
 MTProcs;

// a simple parallel procedure procedure DoSomethingParallel(Index: PtrInt; Data: Pointer; Item: TMultiThreadProcItem); var

 i: Integer;

begin

 writeln(Index);
 for i:=1 to Index*1000000 do ; // do some work

end;

begin

 ProcThreadPool.DoParallel(@DoSomethingParallel,1,10,nil); // address, startindex, endindex, optional data

end. </Delphi>

The output will be something like this:

2
3
1
4
5

Features

Running a procedure in parallel means:

  • a procedure or method is executed with an Index running from an arbitrary StartIndex to an arbitrary EndIndex.
  • One or more threads execute these index' in parallel. For example if the Index runs from 1 to 10, then 3 threads will run three different index at the same time. Every time a thread finishes one call (one index) it allocates the next index and runs it. The result may be: Thread 1 executes index 0,5,7, thread 2 executes 1,3,8,9 and thread 3 runs 2,4,10.
  • The number of threads may vary during run and there is no guarantee for a minimum of threads. In the worst case all index will be executed by one thread.
  • The maximum number of threads is initialized with a good guess for the current system and can be set via
 ProcThreadPool.MaxThreadCount:=8;
  • You can set the maximum threads for each procedure.
  • A parallel procedure (method) can call recursively parallel procedures (methods).
  • Threads are reused, that means they are not destroyed and created for each index or for each procedure, but there is a global pool of threads. On a today common dual core processor there will be two threads doing all the work - the main thread and one extra thread in the pool.

Overhead, slow down

The overhead heavily depends on the system (number and types of cores, type of shared memory, speed of critical sections, cache size). Here are some general hints:

  • Each chunk of work (index) should take at least some milliseconds.
  • The overhead is independent of recursive levels of parallel procedures.

Multi threading overhead, which is independent of the MTProcs units, but simply results from todays computer architectures:

  • As soon as one thread is created your program become multi threaded and the memory managers must use critical sections, which slows down. So even if you do nothing with the thread your program might become slower.
  • The cmem heap manager is on some systems much faster for multi threading. In my benchmarks especially on intel systems and especially under OS X the speed difference can be more than 10 times.
  • Strings and interfaces are globally reference counted. Each access needs a critical section. Processing strings in multiple threads will therefore hardly give any speed up. Use PChars instead.
  • Each chunk of work (index) should work on a disjunctive part of memory to avoid cross cache updates.
  • Do not work on vast amounts of memory. On some systems one thread alone is fast enough to fill the memory bus speed. When the memory bus maximum speed is reached, any further thread will slow down instead of making it faster.


Exceptions

If an exception occur in one of the threads, the other threads will finish normally, but will not start a new Index. The pool waits for all threads to finish and will then raise the exception. That's why you can use try..except like always:

<Delphi> try

 ...
 ProcThreadPool.DoParallel(...);
 ...

except

 On E: Exception do ...

end; </Delphi>

If there are multiple exceptions, only the first exception will be raised. To handle all exceptions, add a try..except inside your parallel method.