01. marts 2009 - 22:36Der er
24 kommentarer og 1 løsning
Delphi tread flere cpu'er
Hej alle sammen
Jeg er igang med at lave et delphi program, som gør brug af treads, men kan ikke rigtig finde ud af, hvorledes man får f.eks. 2 treads til at bruge hver sin CPU, såfremt der er flere?, da det ville sætte lidt speed på mit program.
Håber der er nogen der kan hjælpe med dette, lægger gerne noget kode ud til at teste med.
I dette særtema om aspekter af AI ser vi på skiftet fra sprogmodeller til AI-agenter, og hvordan virksomheder kan navigere i spændet mellem teknologisk hastighed og behovet for menneskelig kontrol.
procedure TMyThread1.Execute; begin FreeOnTerminate := True; EnterCriticalSection(MyLock1); form2.Edit1.Text := 'Thread ' + inttostr(ThreadID) + ' running on CPU ' + inttostr(GetCPUID); LeaveCriticalSection(MyLock1); while FCounter1 < Form2.ProgressBar1.max do begin Synchronize(DoProgress); Inc(FCounter1); Sleep(20); end; MinTread1 := false; end;
procedure TMyThread1.DoProgress; begin Form2.ProgressBar1.Position := FCounter1; end;
procedure TMyThread2.Execute; begin FreeOnTerminate := True; EnterCriticalSection(MyLock2); form2.Edit2.Text := 'Thread ' + inttostr(ThreadID) + ' running on CPU ' + inttostr(GetCPUID); LeaveCriticalSection(MyLock2); while FCounter2 < Form2.ProgressBar2.max do begin Synchronize(DoProgress); Inc(FCounter2); Sleep(30); end; MinTread2 := false; end;
procedure TMyThread2.DoProgress; begin Form2.ProgressBar2.Position := FCounter2; end;
procedure TForm2.Button1Click(Sender: TObject); var T1: TMyThread1; T2: TMyThread2; before, after : integer; begin MinTread1 := true; MinTread2 := true; InitializeCriticalSection(MyLock1); InitializeCriticalSection(MyLock2); T1 := TMyThread1.Create(True); T1.Resume; T2 := TMyThread2.Create(True); T2.Resume; before := GetTickCount; Button1.Caption := 'Slut'; while MinTread1 and MinTread2 do begin Application.ProcessMessages; end; after := GetTickCount; DeleteCriticalSection(MyLock1); DeleteCriticalSection(MyLock2); close; end;
end.
Jeg har lagt det ind som du beskrev, jeg får at vide at hver thread kører på hver sin CPU, men belastnings mæssigt set, er det kun en CPU der bliver belastet, har læst lidt SetThreadAffinityMask, er det ikke den vej man skal?
Så du mener at de kører på samme CPU fordi de bliver oprettet i den samme procedure? Synes ikke rigtig det giver mening, men vil det så sige, at oprettes de via en anden knap, så vil det køre på en anden CPU eller kan du forklare det, så det er til at forstå?
OK, nu tror jeg, at jeg forstår, belastningen med at checke for true er den største belastning, prøver at lægge noget belastning på de 2 thread og undlade sleep og helt afslutte Button1Click
procedure TForm2.Button2Click(Sender: TObject); begin Before := GetTickCount; FCounter := 0; while FCounter < Form2.ProgressBar1.max do begin Inc(FCounter); ireal := FCounter * 500 * cos(30) / 25 / 40 * 2 / cos(30); form2.Edit2.Text := IntToStr(trunc(ireal)); Form2.ProgressBar1.Position := FCounter; Application.ProcessMessages; end; FCounter := 0; while FCounter < Form2.ProgressBar2.max do begin Inc(FCounter); ireal := FCounter * 500 * cos(30) / 25 / 40 * 2 / cos(30); form2.Edit2.Text := IntToStr(trunc(ireal)); Form2.ProgressBar2.Position := FCounter; Application.ProcessMessages; end; FCounter := 0; while FCounter < Form2.ProgressBar3.max do begin Inc(FCounter); ireal := FCounter * 500 * cos(30) / 25 / 40 * 2 / cos(30); form2.Edit2.Text := IntToStr(trunc(ireal)); Form2.ProgressBar3.Position := FCounter; Application.ProcessMessages; end; After := GetTickCount; form2.Edit2.Text := 'Forbrugt tid : ' + IntToStr(After-Before);
end;
end.
Har nu afprøvet ovenstående, med progressbar max 2500 og får på en Quad core´, selve afviklingen af de 3 tråde tager det 1594 og når det køres via botton2 tager det 826, så noget kan tyde på, at programmet ikke bruger 3 cpu'er, kan nogen teste det på en anden computer, idet jeg ikke fatter hvorfor det tager længere tid med brug af tråde
Jeg forstår ikke helt hvad du mener med at de enkelte tråde har interaktion med main tråden, det er sikkert fordi jeg ikke helt forstår det her, håber der er nogen der kan åbenbare dette for mig
Ja, mit program skal bruge meget CPU kraft, så hver tråd vil få hver deres opgave, hvor de enkelte tråd skal levere data til den næste og så fremdeles, opgaven vil formentlig dage en uge eller måske 2, kommer lidt an på, hvor meget CPU kraft man får, det tager ihvertfald mere end en måned ved brug af en enkelt 3 Ghz CPU, så hvis du kan stykke noget sammen, vil jeg blive meget glad, for forstår nemlig ikke hvorfor det ikke bare bruger cpu'erne fuldt ud
Hele pointen er at du sjak lave noget CPU intensivt for at brug kernerne.
Prøv og køre følgende og se om den kan få alle dine kerner i brug:
program thrfun2;
{$APPTYPE CONSOLE}
uses Classes, SysUtils, Windows;
type TMyThread1 = class(TThread) protected procedure Execute; override; end; TMyThread1Range = 1..8; TMyThread1Array = array [TMyThread1Range] of TMyThread1;
procedure TMyThread1.Execute;
const REP = 100; N = 1000000000;
var sum, i, j : integer;
begin for J := 1 to REP do begin sum := 0; for i := 0 to (N - 1) do begin sum := ((sum + 1) * 2 + 1) div 2; end; if sum <> N then writeln('Houston we have a problem');; end; end;
var T : TMyThread1Array; i : TMyThread1Range;
begin for i := 1 to 8 do T[i] := TMyThread1.Create(True); for i := 1 to 8 do T[i].Resume; for i := 1 to 8 do T[i].WaitFor; for i := 1 to 8 do T[i].Destroy; end.
OK mange tak Arne, jeg har puttet indholdet af din tråd ind i hver af mine 3 tråde og jeg får fuld CPU belastning på alle 4 cpu'er.
Kan du forklare lidt om hvad det betyder at tråden udfører interaktion med main tråden, har testet at application.ProcessMessages, Synchronize(DoProgress) og opdatering edit.text tager en masse CPU kraft, men gælder det også globe variable, som jeg vil bruge til at styre de 3 tråde ?
Smid et svar Arne, det har bragt mig til en vis forståelse for hvad man kan/må putte ind i en tråd.
Hvis det ellers lige nogle guldkorn omkring generell brug af tråde, ja så lytter jeg gerne´.
for at forhindre trådene i at gnaske masser af CPU i sig.
Men hvis du siger at de er OK, så er forskellen nok snarere at min kode have en meget tung beregning.
Tilgang til globale variable koster ikke i sig selv noget, men den synkronisering du skal bruge for at undgå concurrency problemer kan godt koste noget. Så det skal du designe forsigtigt.
Jeg forstår ikke helt dine kvaler .. Det er bare at sætte trådene i gang så klarer Windows resten.
Jeg har lavet et eksempel her : borrisholt.dk/Eksperten/Thread.zip
Lidt GUI og lidt "matematik"
Hvad angår spil2vind / ArneV forslag så vil de give en AV idet GetCPUID piller i EBX registeret uden at gemme den orginale værdi først ...
En løsning kunne se således ud :
onst ID_BIT = $200000; // EFLAGS ID bit type TVendor = array[0..11] of Char; TCPUID = array[1..4] of Longint;
function IsCPUID_Available: Boolean; register; asm PUSHFD {direct access to flags no possible, only via stack} POP EAX {flags to EAX} MOV EDX,EAX {save current flags} XOR EAX,ID_BIT {not ID bit} PUSH EAX {onto stack} POPFD {from stack to flags, with not ID bit} PUSHFD {back to stack} POP EAX {get back to EAX} XOR EAX,EDX {check if ID bit affected} JZ @exit {no, CPUID not availavle} MOV AL,True {Result=True} @exit: end;
function GetCPUID: TCPUID; assembler; register; asm PUSH EBX {Save affected register} PUSH EDI MOV EDI,EAX {@Resukt} MOV EAX,1 DW $A20F {CPUID Command} STOSD {CPUID[1]} MOV EAX,EBX STOSD {CPUID[2]} MOV EAX,ECX STOSD {CPUID[3]} MOV EAX,EDX STOSD {CPUID[4]} POP EDI {Restore registers} POP EBX end;
Skal det så omformes til en streng kunne man gøre noget alla det her :
function GetCPUInfo: string; var CPUID: TCPUID; I: Integer; S: TVendor; begin for I := Low(CPUID) to High(CPUID) do CPUID[I] := -1; if IsCPUID_Available then begin CPUID := GetCPUID; S := GetCPUVendor; Result := IntToHex(CPUID[1], 8) + '-' + IntToHex(CPUID[2], 8) + '-' + IntToHex(CPUID[3], 8) + '-' + IntToHex(CPUID[4], 8); end else Result := 'CPUID not available'; end;
Grunden til jeg ikke har det med i mit eksempel er fordi jeg ikke lige kunne få funktionerne til at exekvere fra andet en mail tråden, fordi så passede register værdierne ikke, og jeg gad ikke at debugge ;)
Det samme vil gøre sig gældende for spil2vind / ArneV hvis ellers man rettede den AV så vil CPUID være det der tilhørte main tråden, fordi det er i den koden er exekveret.
Jeg skrev at ProcessMessages og Synchronize(DoProgress) gjorde at løkken blev bremset, men ok, jeg fik jo styr på hvorledes man skal bruge flere tråde, men forstår ikke helt Boris, dit svar kan jeg ikke se, at det skulle give en større forståelse for brug af tråde, det er vel i grunden lige gyldigt hvilken cpu tråden kører? forstår udfra det Arne skriver at windows selv vælger den cpu der er mindst belastet eller fortolker jeg helt forkert?
Hvis windows ikke selv placer trådene på forskellige cpu'er vil det jo være nødvendigt at vide hvorledes man tildeler tråde til hver sin cpu.
spil2vind>>Det er fuldstendig korrekt ... Den eneste grund til jeg begynte at intressere mig for hvilken tråd der kørte på hvilken CPU var fordi dig/arne v begynte ... Jeg ville jeg bare gøre opmærksom på ar jeres måde at hente CPUID var forkert.
Hvis du henter mit eksempel på http://borrisholt.dk/Eksperten/Thread.zip vil du også opdage at i det er der vist hvordan man arbejder med tråde, og hvordan man får det synkroniseret med main tråden.
Jeg har heller ikke noget CPU halløj i mit eksempel.
D ogsåopdage at Application.ProcessMessages er forkert at kalde inde i en tråd. Dels er det 100% overflødigt og dels er resultatet af kaldet uforudsigeligt ...
Det der tilgændgæld er 100% forudsigeligt er at hvis du piller i EBX registeret UDEN at gemme det først og restore værdien bagefter så får du en AV ganske som tidligere belyst.
Nu brugte jeg nu bare ProcessMessages til at kunne se fremskridt på skærmen, at det ødelagde tråden fandt jeg så udaf, men tak for dine input, rart at få info omkring brug af tråde, samt de fælder der nu er, samt andres erfaringer, glæder mig faktisk til at afprøve det med min opgave, som har en masse ting, som kan opdeles i tråde, dog er opgaven jo noget svær, idet den enkelte tråd, skal afvente at den forrige er færdig eller har fundet en løsning til opgaven.
Imidlertid gav den ingen fejl i eksemplet, så det problem opdagede jeg ikke. EBX må tilsyneladende ikke blive brugt i den kode. Men det kunne den jo nemt blive i noget andet kode.
Funktionen bliver kaldt i de startede tråde ikke i main tråd.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.