Autor Beitrag
GuaAck
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 376
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: Fr 24.05.13 20:58 
Hallo,

ich habe einige bewährte rechenintensive Programme, die ich so ändern will, dass bei modernen Multi-Core Rechnern alle Kerne rechnen. Ich muss also die Arbeit auf mehrere Threads aufteilen.

Aber mit mehreren Threads ist ja Overhead verbunden, deshalb mein Frage an folgendem Beispiel:

Ich möchte rechnen:

x=f1(a,b,c...)
y=f2(a,b,c...)
z=x*y

(f1, f2 sind Funktionen mit mehr oder weniger komplizierten Berechnungen)

x und y könnten gut in verschiedenen Threads berechnet werden, z wird dann berechnet, wenn beide Threads melden, dass sie fertig sind.

Meine Frage: Wie zeitaufwändig müssen f1 und f2 sein, damit sich die Verteilung auf Threads überhaupt lohnt? Kennt da jemand Richtwerte, Faustformeln o.ä.? Hängt wohl auch mit der Verwaltung unter Windows zusammen.

Gruß GuaAck
IhopeonlyReader
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Fr 24.05.13 21:16 
Nach dem Delphi-Treff ist ein extra Thread nicht schneller !, da der Arbeitsspeicher aufgeteilt wird..
ob das stimmt, weiß ich nicht...
probiere es aus ;)
Delphi- Threads sind ja recht einfach und schnell zu programmieren

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 24.05.13 21:36 
user profile iconIhopeonlyReader hat folgendes geschrieben Zum zitierten Posting springen:
Nach dem Delphi-Treff ist ein extra Thread nicht schneller !, da der Arbeitsspeicher aufgeteilt wird..
Das ist Blödsinn. Der Arbeitsspeicher ist bei viel benötigter Rechnerleistung irrelevant. Da geht es nur darum, dass ein einzelner Thread nur auf einem Prozessorkern ausgeführt wird, mehrere Threads aber auf die Kerne verteilt werden und alle Kerne auslasten.

Ob das für eine bestimmte Aufgabenstellung etwas bringt, hängt davon ab, ob man Berechnungen hat, die eine Weile dauern. Denn nur dann lohnt es sich die auf Threads zu verteilen.
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Sa 25.05.13 11:33 
Tja, also das kommt auch ganz erheblich darauf an, wie du die Threads verwendest. Ständig neue zu erzeugen kostet nämlich auch wieder Zeit, schlafen legen und bei Bedarf aufwecken ist vergleichsweise günstig.

Ich habe mal meinen pi-Transformator laufen lassen und die Zeit für einzelne Iterationen 'rausgeschrieben. Bei dieser Implementierung werden mehrere Task<> Objekte gestartet, auf die dann gewartet wird - die eigentlichen Threads leben also lange und werden nicht ständig neu erzeugt.

Da sich diese Berechnung sehr gut parallelisieren lässt, lohnt es sich quasi immer:

parallelisierung

Auf der Y-Achse sind Ticks, d.h. 100 Ticks sind 10 Mikrosekunden, 1000 Ticks entsprechend 0,1 Millisekunden. Die einzelnen Arbeitspakete sind also relativ klein.

Um auf deine Frage näher einzugehen: Wenn die Threads bereits erstellt sind und nur noch auf die Arbeit warten, dann dürfen x und y auch kleinere Arbeitsbrocken sein ohne dass es langsam wird.
Die Werte an den Rändern sind wie folgt:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
Ziffern  4-Threads  1-Thread
29438      420        1155
29421      394        1151
29404      388        1150
29386      391        1149
....
998        29          39
981        32          39
964        31          38
946        30          37
929        26          36
912        25          36
895        25          37
878        26          35

Die Multithreaded-Variante ist bei wenig Arbeit also immer noch schneller - wenn auch nicht viermal so schnell - wie die einfache Variante. Die große Streuung bei der 4-Thread-Variante deutet vermutlich darauf hin, dass ich die TPL noch nicht richtig bediene und irgendwo noch etwas dazwischen funkt. Mehr zu dem "Problem" hier: www.entwickler-ecke....rder=asc&start=0
Einloggen, um Attachments anzusehen!


Zuletzt bearbeitet von jfheins am Di 06.08.13 09:13, insgesamt 1-mal bearbeitet

Für diesen Beitrag haben gedankt: GuaAck, Marc.
GuaAck Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 376
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: So 26.05.13 00:17 
Hallo jfheins,

tolle Übersicht, Danke nochmals. Genau das suchte ich.

Vermutlich teilst Du ja von einem Hauptprogramm aus den einzelen Threads ihre Rechenaufgaben (Zahlenbereiche) zu. Auf welche Art meldet ein Thread dem Hauptprogramm, dass er fertig ist und neue Arbeit braucht? Ich habe bisher im Hauptprogramm einfach zyklisch per Timer ein Flag abgefragt oder auch vom Thread aus eine Message geposted, beides wohl nicht besondes effizient.

Ansonsten habe ich die Threads einmalig erzeugt, per "execute" zum Rechnen veranlasst und sie dann per "suspend" wieder schlafen gelegt.

Gruß
GuaAck
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: So 26.05.13 00:52 
Das wird dir jetzt leider nicht viel helfen, weil das in C# programmiert ist.

Das "starten" sieht so aus:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
cts = new CancellationTokenSource(); // Damit kann der Berechnungsvorgang wieder abgebrochen werden
progressBar1.Maximum = 1;

var calculator = new Task(() => Calculate(_digits, _Numelems), cts.Token);
calculator.ContinueWith((x) => DisplayResult(x));
calculator.Start();

Es wird also ein neuer Task gesartet (=unabhängig von der GUI) und eine Arbeiter-Methode angegeben (Calculate()), sowie eine Methode die ausgeführt wird wenn er fertig ist (DisplayResult(x)).

Damit ist jetzt erstmal die Berechnung vom Hauptthread entkoppelt. Die Aufteilung in vier Teile geschieht so:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
private void Calculate(int digits, int numelems)
{
  for (int i = 0; i < digits; i++)
  {
    // In vier Teile teilen
    var pivot1 = (int)(numelems * 0.25);
    var pivot2 = (int)(numelems * 0.50);
    var pivot3 = (int)(numelems * 0.75);        
    // Die vier Teile anstoßen
    var Parts = new Task<ulong>[4];
    Parts[0] = Task.Factory.StartNew<ulong>(() => MultiplyCarryover(0, pivot1));
    Parts[1] = Task.Factory.StartNew<ulong>(() => MultiplyCarryover(pivot1 + 1, pivot2));
    Parts[2] = Task.Factory.StartNew<ulong>(() => MultiplyCarryover(pivot2 + 1, pivot3));
    Parts[3] = Task.Factory.StartNew<ulong>(() => MultiplyCarryover(pivot3 + 1, numelems));

    Task.WaitAll(Parts);
  
    // Der serielle Teil, der nicht parallelisiert werden kann
    AddCarry(pivot3, Parts[3].Result);
    AddCarry(pivot2, Parts[2].Result);
    AddCarry(pivot1, Parts[1].Result);

    result[i] = Parts[0].Result;

    // Fortschrittsbaleken aktualisieren, ggf. abbrechen
    if (i % 500 == 0)
    {
      this.Dispatcher.BeginInvoke(new Action(() => DisplayProgress(m * i + 5)));
      if (cts.IsCancellationRequested)
      {
        _digits = m * i + 5;
        return;
      }
    }
  }
}


Du siehst, ich gehe relativ großzügig mit dem Erstellen der Task<> Objekte um, weil ein neues Task<> Objekte eben nicht zwangsweise einen neuen Thread erzeugt. Das eigentliche Warten geht wohl über Windows' waitformultipleobjects-Funktion. Aber das mit dem Message-posten müsste eigentlich auch gut funktionieren.


Zuletzt bearbeitet von jfheins am Mi 29.05.13 12:46, insgesamt 1-mal bearbeitet

Für diesen Beitrag haben gedankt: GuaAck
GuaAck Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 376
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: So 26.05.13 23:12 
Hallo jfheins,

der C-Code hat mir gut geholfen. Ich habe das mal so ähnlich umgesetzt und etwas experimentiert, lief ganz gut.

Danke jedenfalls,
Gruß
GuaAck