Parallele Abarbeitung einer Schleife in C++

Für allgemeine Fragen zur Programmierung, welche nicht! direkt mit Lazarus zu tun haben.
Antworten
Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Parallele Abarbeitung einer Schleife in C++

Beitrag von Mathias »

Ich habe ein Fraktal-Programm von C++ auf Pascal übernommen,
Dabei habe ich festgestellt, das die Pascal-Variante etwas über 10x langsamer war.
Zuerst dachte ich, ich habe etwas falsch übernommen, oder FPC hat meine Vektor Arithmetik schlecht übersetzt.
Ich dachte auch, das der Compiler MMX, SSE oder was ähnliches verwendet.
Aber als das Programm aber folgendes ausspuckte, da muss was anderes im Spiel sein.

Code: Alles auswählen

j: 480
j: 1080
j: 120
j: 180
...

Code: Alles auswählen

#pragma omp parallel for
    for (int j = 0; j<height; j++) {
    printf("j: %d\n", j);  // Als Test

        for (int i = 0; i<width; i++) {
          // Rechne was intensives
        }
    }
Da habe ich noch folgende ominöse Zeile entdeckt.

Code: Alles auswählen

#pragma omp parallel for
Damit kann man sehr einfach mehrere Prozesskerne ausnutzen, ohne das man sie manuell mit Threading auseinander setzen muss.

Da ist wohl C++ an FPC überlegen.
Oder gibt es in FPC auch so etwas, was ich mir schlecht vorstellen kann ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Parallele Abarbeitung einer Schleife in C++

Beitrag von Warf »

Mathias hat geschrieben:
Do 7. Sep 2023, 08:39
Da ist wohl C++ an FPC überlegen.
Oder gibt es in FPC auch so etwas, was ich mir schlecht vorstellen kann ?
Pragma OMP ist kein C++ feature, das ist ein OpenMP feature was manche C++ compiler über Pragmas integrieren.

Die C++ Native lösung wäre:

Code: Alles auswählen

std::for_each(
  std::execution::par, // Parallele Ausführung
  0, // Start wert (inklusive)
  height, // End wert (exklusive)
  [&](auto j) { // Lambda für loop körper
      //...
  }
);
Delphi kann das übrigens auch:

Code: Alles auswählen

TParallel.For(
  0, // start wert (inklusive)
  height-1, // end wert (inklusive)
  procedure(j: Integer) // Lambda für loop körper
  begin
    // …
  end
);
Ob FPC das auch kann, keine Ahnung, aber mit den function references in Trunk sollte sowas eigentlich auch möglich sein

sstvmaster
Beiträge: 576
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: W10, L 2.2.6
CPU-Target: 32+64bit
Wohnort: Dresden

Re: Parallele Abarbeitung einer Schleife in C++

Beitrag von sstvmaster »

LG Maik

Windows 10,
- Lazarus 2.2.6 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.7 (fixes) + fpc 3.3.1 (main/trunk)

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Parallele Abarbeitung einer Schleife in C++

Beitrag von Mathias »

Ob FPC das auch kann, keine Ahnung, aber mit den function references in Trunk sollte sowas eigentlich auch möglich sein
Das fährt sich ja voll mit gelöster Handbremse.

Die Package MultiThreadProcsLaz ist bei meiner Trunk dabei, man muss sie nur über "Package" aktivieren.
Und im eigenen Project noch unter "Neue Anforderung" einbinden.
und bei uses noch "MTProcs" einbinden.

Das sieht dann vereinfacht so aus..

Code: Alles auswählen

procedure doParallel(Index: PtrInt; Data: Pointer; Item: TMultiThreadProcItem);
var
  bit: TBitmap;
  ...
 
begin
  bit := TBitmap(Data);
  ...  
    fb := palette_fire((-0.2 + noise_level) * 2) * light_intensity;
  ....    
  for j := 0 to 2 do begin
    bit.RawImage.Data[Index * 4 + j] := max(0, min(255, Round(255 * fb[j])));
  end;
  bit.RawImage.Data[Index * 4 + 3] := $FF;
end;

procedure TForm1.MenuItem1Click(Sender: TObject);
begin
  Image1.Picture.Bitmap.Width := Width;
  Image1.Picture.Bitmap.Height := Height;
  Image1.Picture.Bitmap.pixelformat := pf32bit;

  ProcThreadPool.DoParallel(@doParallel, 0, Width * Height - 1, pointer(Image1.Picture.Bitmap));
end; 
Diese Lösung sieht sogar eleganter aus, als die von C++.

Nachtrag:
Es blockiert während der Berechnung das ganze Form.
Ein "Application.ProcessMessages" erlaubt er mir in der Schleife nicht, das Programm bleibt hängen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Parallele Abarbeitung einer Schleife in C++

Beitrag von Warf »

Mathias hat geschrieben:
Do 7. Sep 2023, 15:51
Nachtrag:
Es blockiert während der Berechnung das ganze Form.
Ein "Application.ProcessMessages" erlaubt er mir in der Schleife nicht, das Programm bleibt hängen.
Naja das ist ganz einfach, der Mainthread wird auch verwendet für den For loop. Das ermöglicht dir aber auch auf diesem dann Application.ProcessMessages aufzurufen:

Code: Alles auswählen

threadvar IsMainThread: Boolean;

procedure TForm1.Button1Click(Sender: TObject);
begin
  IsMainThread:=True; // Wird nur auf dem Hauptthread gesetzt, auf allen anderen ist es false
  ProcThreadPool.DoParallel(@DoNothing, 0, 100);
  ShowMessage('Done');
end;

procedure TForm1.DoNothing(Index: PtrInt; Data: Pointer;
  Item: TMultiThreadProcItem);
var
  i: Integer;
begin
  for i:=0 to 100 do
  begin
    if IsMainThread then // Ist nur auf dem Mainthread wahr
      Application.ProcessMessages;
    Sleep(10);
  end;
end;

Antworten