Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Multithreading (https://www.delphipraxis.net/213400-multithreading.html)

Gruber_Hans_12345 24. Jul 2023 12:35

Multithreading
 
Hallo kann mal wer einen Blick auf meinen Code werfen - irgendwo habe ich da einen Denkfehler.

Das ist meine Threadklasse
Delphi-Quellcode:
type
    TTestThread = class(TThread)
    protected
        fWaitFinish        : THandle;
        fResumeEvent       : THandle;
        procedure Execute; override;
    public
        constructor Create();
        destructor Destroy; override;
    end;

destructor TTestThread.Destroy;
begin
    CloseHandle(fWaitFinish);
    CloseHandle(fResumeEvent);
    inherited;
end;

constructor TTestThread.Create();
var
    i          : integer;
begin
    fResumeEvent       := 0;
    fWaitFinish            := CreateEvent(nil, TRUE, FALSE, nil);
    FreeOnTerminate    := FALSE;
    inherited Create(TRUE);
end;

procedure TTestThread.Execute;

    procedure internalExecute;
    var
        i      : integer;
        x      : Byte;
    begin
        x  := 9;
        for i:=1 to 10000000 do begin
            x  := x xor (Random(9999999) mod 255);
        end;
        SetEvent(fWaitFinish);
    end;

begin
    fResumeEvent   := CreateEvent(nil, TRUE, FALSE, nil);
    repeat
        internalExecute;
        if Terminated then break;
        WaitForSingleObject(fResumeEvent, INFINITE);
        ResetEvent(fResumeEvent);
    until Terminated;
    SetEvent(fWaitFinish);
end;
Und so rufe ich das mit einer Art Threadpool auf

Delphi-Quellcode:
procedure TfrMDIChild.Button19Click(Sender: TObject);
var
    maxThreads     : integer;
    tempFunc       : TInterpreterFunDesc;
    hArrWait       : array of THandle;
    threadList     : array of TTestThread;

    function GetIdleThread : integer;
    var
        i          : integer;
    begin
        for i:=0 to maxThreads-1 do
            if hArrWait[i] = 0 then begin
                threadList[i]          := TTestThread.Create();
                hArrWait[i]            := threadList[i].fWaitFinish;
                Result := i;
                exit;
            end;
        repeat
            Result := WaitForMultipleObjects(length(hArrWait), @hArrWait[0], FALSE, INFINITE);
        until (Result >= 0) and (Result < maxThreads);
        Result := Result - WAIT_OBJECT_0;
    end;

    procedure InitThreads;
    var
        i      : integer;
    begin
        setlength(threadList, maxThreads);
        setlength(hArrWait, maxThreads);
        for i:=0 to maxThreads-1 do
            hArrWait[i]    := 0;
    end;
   
var
    i              : integer;
    threadIdx      : integer;
    perfFreq       : int64;
    perfStart      : int64;
    perfEnd        : int64;
begin
    QueryPerformanceFrequency(perfFreq);

    QueryPerformanceCounter(perfStart);
    maxThreads     := TButton(Sender).Tag;
    InitThreads;
    for i:=0 to 100 do begin
        threadIdx  := GetIdleThread;
        if threadList[threadIdx].fResumeEvent = 0 then begin
            threadList[threadIdx].Resume;
        end
        else begin
            ResetEvent(threadList[threadIdx].fWaitFinish);
            SetEvent(threadList[threadIdx].fResumeEvent);
        end;
    end;
    QueryPerformanceCounter(perfEnd);

    setlength(threadList, 0);
    setlength(hArrWait, 0);
    QueryPerformanceCounter(perfEnd);
    Memo1.Lines.Add('Threading ('+IntToStr(maxThreads)+' Threads : '+FormatFloat('0.00', (perfEnd-perfStart) * 1000 / perfFreq)+' ms');
end;
Den Testbutton rufe ich einmal mit einem Thread auf und einmal das er acht Threads machen soll
Das Ergebnis ist dann
Code:
Threading (1 Threads : 4446,57 ms
Threading (8 Threads : 5980,15 ms
Was übersehe ich da das er bei 8 gleichzeitigen Threads die Dauer so lange ist? (Die CPU Auslastung geht aber da schön auf fast 100% hoch)

Stevie 24. Jul 2023 13:55

AW: Multithreading
 
Wie viele Kerne hat die CPU, auf der du das ausführst?

Gruber_Hans_12345 24. Jul 2023 13:58

AW: Multithreading
 
Entwicklungsrechner ist ein virtualisierter mit 4 Kerne
Aber auch auf meinem Rechner hier dann direkt die EXE getestet und der hat 8 "echte" Kerne.
Bei beiden das gleiche - mit einem Thread ist es am schnellsten

fisipjm 24. Jul 2023 13:59

AW: Multithreading
 
Hey Hans,

wenn ich deinen Code beim Überfliegen richtig verstanden habe, erzeugst du doch im
Delphi-Quellcode:
InternalExecute
Teil immer die gleiche Aufgabe, für egal wie viele Threads. Spich jeder Thread hat die gleich Aufgabe und bei dem 8 Thread Pool kommt dann eben noch der Overhead drauf für das Threadhandeling.

Oder hab ich da was übersehen?


Edit: Code nicht verstanden :oops:

Stevie 24. Jul 2023 14:04

AW: Multithreading
 
Wenn er 8 Kerne hat, dann musst du schon sicher stellen, das gar nix anderes läuft, damit er dieselbe Aufgabe 8mal durchführen kann auf 8 Threads, damit er genauso schnell ist, wie die Aufgabe 1mal auszuführen.
Ich habe das bei mit einem 12Kerner versucht bei mir (i7-12700, was nen 12-Kerner mit 8 P-Cores ist) und sehe in etwa die selbe Dauer mit einem oder mit acht Threads.

Gruber_Hans_12345 24. Jul 2023 14:08

AW: Multithreading
 
JA es soll im prinzip ienfahc 100 mal der code vom internalExecute ausgeführt werden - in dem Fall eine einfachste Version - die nichts anderes macht ausser primitive Berechnungen.

Daher sollte er mit 8 Threads ja meiner Meinung nach 8 mal so schnell sein wie mit einem Thread - okay ganzer Overhead und co aber zumindest 4 mal so schnell.

Und nicht wie in meinem Fall das es sogar langsamer ist diese 100 mal internalExecute auszuführen

dummzeuch 24. Jul 2023 14:08

AW: Multithreading
 
Was fisipjm schon schrieb: Solange Du die auszüführende Aufgabe nicht auf mehrere Threads verteilst sondern jeden Thread die komplette Aufgabe machen lässt, wird sich die Laufzeit nicht ändern. Im Gegenteil: Jeder zusätzliche Thread erhöht den Overhead.

Edit: Mist, gepennt. Man sollte nicht kommentieren, wenn man den Code nicht verstanden hat.

Gruber_Hans_12345 24. Jul 2023 14:10

AW: Multithreading
 
Hmm verstehe ich nicht ganz

Ich möchte 100 mal die funktion internalExecute aufrufen

bei einem thread wird der eine Thread das 100 mal aufrufen

bei 8 Threads verteilt es sich, und im idealfall muss jeder Thread die funktion nur 12.5 mal aufrufen

himitsu 24. Jul 2023 14:11

AW: Multithreading
 
Mir war so, als sei der Zufallsgenerator je Thread unabhängig,
aber wenn es nur einen Globalen gäbe, dann wäre die Sache klar.

* in aufgerufenen Funktionen kann eine Synchronisierung drin sein
* und dann die Speicherzugriffe ... wenn alle Kerne auf den selben Speicher zugreifen, dann blocken viele CPUs hier auch gern

Gruber_Hans_12345 24. Jul 2023 14:14

AW: Multithreading
 
Zitat:

Zitat von himitsu (Beitrag 1524839)
Mir war so, als sei der Zufallsgenerator je Thread unabhängig,
aber wenn es nur einen Globalen gäbe, dann wäre die Sache klar.

* in aufgerufenen Funktionen kann eine Synchronisierung drin sein
* und dann die Speicherzugriffe ... wenn alle Kerne auf den selben Speicher zugreifen, dann blocken viele CPUs hier auch gern

Danke das wars
Ohne Random verhält es sich nun wie ich es erwarte ...
Komisch im Source vom Randsom sieht man keine CriticalSections oder co


Aber nun kann ich weiter testen danke


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:23 Uhr.
Seite 1 von 3  1 23      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz