Sign in Join
Wiki copytemporary

Parallel procedures/de: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Übersicht
22
33Diese Seite beschreibt, wie man eine einzelne Prozedur parallel laufen lässt unter Benutzung der MTProcs Unit, die das parallel laufen lassen und die Implementierung von parallelen Algorithmen vereinfacht.
44
55Parallele Prozeduren und Methoden werden oft in parallele Algorithmen gefunden und einige Programmiersprachen haben einen eingebauten Support für sie (z.B. OpenMP im gcc). Siehe auch [[OpenMP support|hier]] für die Planung der Hinzufügung dieser Sprachfeatures im FPC. Einbetten dieser Sachen in die Sprache kann zur Reduzierung von Eingaben führen und erlaubt dem Compiler Code zu erzeugen, der weniger Overhead hat. Auf der anderen Seite ist hier kein einfacher Algorithmus, um Code parallel laufen zu lassen. Tatsächlich machen einfache Versuche oft den Code langsamer. Um gute Resultate zu erhalten, muss man einige Parameter spezifizieren, die der Compiler nicht voraussagen kann. Als Beispiel kann man sich die unermessliche Anzahl an Einstellungen und Diskussionen auf [OpenMP](http://openmp.org/wp/) ansehen. Man benötigt parallele Algorithmen. MTProcs hilft bei der Implementierung von parallelen Algorithmen.
66
77## MTProcs erhalten
88
99Die Unit **mtprocs.pas** ist ein Teil des **multithreadprocslaz.lpk** Paket, das mittels svn von
1010
1111 svn co https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/components/multithreadprocs multithreadprocs
1212
1313heruntergeladen werden kann.
1414
1515Wie immer: Öffne das Paket multithreadprocslaz.lpk einmal in der IDE, so das es die Pfade lernt. Um das Paket in deinem Projekt zu verwenden: Benutze *IDE Menu / Package / Open recent package / .../multithreadprocslaz.lpk / More / add to project* um es in deinem aktuellen Projekt zu verwenden. Oder füge mittels des Projekt Inspektors hinzu.
1616
1717## Einfaches Beispiel
1818
1919Hier ist ein einfaches Beispiel, das nichts wirklich sinnvolles macht, um zu zeigen, wie parallele Prozeduren aussehen und wie sie aufgerufen werden:
2020
2121``` pascal
2222program Test;
2323
2424{$mode objfpc}{$H+}
2525
2626uses
2727 {$IFDEF UNIX}
2828 cthreads, cmem,
2929 {$ENDIF}
3030 MTProcs;
3131
3232// a simple parallel procedure
3333procedure DoSomethingParallel(Index: PtrInt; Data: Pointer; Item: TMultiThreadProcItem);
3434var
3535 i: Integer;
3636begin
3737 WriteLn(Index);
3838 for i := 1 to Index * 1000000 do ; // do some work
3939end;
4040
4141begin
4242 ProcThreadPool.DoParallel(@DoSomethingParallel, 1, 5, nil); // address, startindex, endindex, optional data
4343end.
4444```
4545
4646Die Ausgabe wird ähnlich wie folgendes sein:
4747
4848 2
4949 3
5050 1
5151 4
5252 5
5353
5454Hier einige kurze Anmerkungen. Die Details folgen später
5555
5656- Multithreading benötigt unter Unix die Unit **cthreads** wie im [[Multithreaded Application Tutorial/de]] erklärt.
5757- Aus Geschwindigkeitsgründen wird der **cmem** Heapmanager empfohlen, aber hier für dieses Beispiel macht es keinen Unterschied.
5858- Die parallele Procedure **DoSomethingParallel**erhält einige fixe und vordefinierte Parameter.
5959- **Index** definiert, welcher Teil der Arbeit in diesem Aufruf gemacht werden soll.
6060- **Data** ist ein Zeiger, der *ProcThreadPool.DoParallel* als vierter Parameter übergeben wird. Er ist optional und man ist frei, ob man ihn für irgendwas benutzt.
6161- **Item** kann für den Zugriff auf mehr verfeinerte Features des Threadpools verwendet werden.
6262- **ProcThreadPool.DoParallel** arbeitet wie ein normaler Proceduraufruf. Er kommt zurück, wenn er komplett abgearbeitet wurde - das heißt, alle Threads haben ihre Arbeit beendet.
6363- Die Ausgabe zeigt den typischen multithreading Fall: Die Reihenfolge der Aufrufe ist nicht vorhersehbar. Verschieden Läufe können Ergebnisse in unterschiedlicher Reihenfolge haben.
6464
6565## Features
6666
6767Running a procedure in parallel means:
6868
6969- a procedure or method is executed with an Index running from an arbitrary StartIndex to an arbitrary EndIndex.
7070- One or more threads execute these index' in parallel. For example if the Index runs from 1 to 10 and there are 3 threads available in the pool, 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 3,5,7, thread 2 executes 1,6,8,9 and thread 3 runs 2,4,10.
7171- 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 thread itself.
7272- The maximum number of threads is initialized with a good guess for the current system. It can be manually set at any time via
7373
74-` ProcThreadPool.MaxThreadCount:=8;`
74+``` pascal
75+ ProcThreadPool.MaxThreadCount:=8;
76+```
7577
7678- You can set the maximum threads for each procedure.
7779- A parallel procedure (or method) can call recursively parallel procedures (or methods).
7880- Threads are reused, that means they are not destroyed and created for each index, but there is a global pool of threads. On a dual core processor there will be two threads doing all the work - the main thread and one extra thread in the pool.
7981
8082## Overhead, slow down
8183
8284The 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:
8385
8486- Each chunk of work (index) should take at least some milliseconds.
8587- The overhead is independent of recursive levels of parallel procedures.
8688
8789Multi threading overhead, which is independent of the MTProcs units, but simply results from todays computer architectures:
8890
8991- As soon as one thread is created your program becomes 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.
9092- 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.
9193- 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.
9294- Each chunk of work (index) should work on a disjunctive part of memory to avoid cross cache updates.
9395- 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.
9496
9597## Setting the maximum number of threads for a procedure
9698
9799You can specify the maximum number of threads for a procedure as the fifth parameter.
98100
99101``` pascal
100102begin
101103 ProcThreadPool.DoParallel(@DoSomethingParallel, 1, 10, nil, 2); // address, startindex, endindex,
102104 // optional: data (here: nil), optional: maximum number of threads (here: 2)
103105end.
104106```
105107
106108This can be useful, when the threads work on the same data, and a too many threads will create so many cache conflicts, that they slow down each other. Or when the algorithm uses a lot of WaitForIndex, so that only a few threads can actually work. Then the threads can be used for other tasks.
107109
108110## Wait for index / executing in order
109111
110112Sometimes an Index depends on the result of a former Index. For example a common task is to first compute chunk 5 and then combine the result with the result of chunk 3. Use the WaitForIndex method for that:
111113
112114``` pascal
113115procedure DoSomethingParallel(Index: PtrInt; Data: Pointer; Item: TMultiThreadProcItem);
114116begin
115117 ... compute chunk number 'Index' ...
116118 if Index = 5 then begin
117119 if not Item.WaitForIndex(3) then exit;
118120 ... compute ...
119121 end;
120122end;
121123```
122124
123125WaitForIndex takes as an argument an Index that is lower than the current Index or a range. If it returns true, everything worked as expected. If it returns false, then an exception happened in one of the other threads.
124126
125127## Exceptions
126128
127129If 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:
128130
129131``` pascal
130132try
131133 ...
132134 ProcThreadPool.DoParallel(...);
133135 ...
134136except
135137 On E: Exception do ...
136138end;
137139```
138140
139141If there are multiple exceptions, only the first exception will be raised. To handle all exceptions, add a try..except inside your parallel method.
140142
141143---
142144*From the Free Pascal wiki, page "Parallel procedures/de", as it stood on 2020-02-23 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history