![]() |
Delay zu langsam?
Hallo DP,
wenn richtig viel los ist, ist Delay von Hagen recht langsam. Jedenfalls viel langsamer als ich erwartet hätte. Folgende Schleife dauert mit Sleep und Application.ProcessMessages ca. 5000ms. Die gleiche Schleife dauert ca. 7500ms mit Delay, wenn man etwas mit der Maus "rumrührt".
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var i: Integer; begin btnResetClick(Self); btnStartClick(Self); for i := 1 to 500 do // ungefähr 5 Sekunden, sollte man meinen. begin // Delay(10); Sleep(10); Application.ProcessMessages; end; btnStopClick(Self); end;
Delphi-Quellcode:
Ist an meinem Delay was falsch oder ist das normal und so gewollt?
procedure Delay(Milliseconds: integer);
var Tick: DWORD; Event: THandle; begin Event := CreateEvent(nil, False, False, nil); try Tick := GetTickCount + DWORD(Milliseconds); while (Milliseconds > 0) and (MsgWaitForMultipleObjects(1, Event, False, Milliseconds, QS_ALLINPUT) <> WAIT_TIMEOUT) do begin Application.ProcessMessages; if Application.Terminated then Exit; Milliseconds := Tick - GetTickCount; end; finally CloseHandle(Event); end; end; Grüße, Uwe EDIT: Liegt wohl an der zu geringen Auflösung von 10ms... |
AW: Delay zu langsam?
Hi
in deinem Test hast du meine Delay() Funktion ausgeklammert. Ich nehme also an du hast das mal so getestet das du das Sleep(10) auskommentierst und stattdessen mein Delay(10) aktiviert hast ? Wenn dem so ist ist die Frage ob du das nachfolgende Application.ProcessMessages ebenfalls auskommentiert hast ? Wenn nicht ist dein "Performancevergleich" falsch da mein Delay() ja schon .ProcessMessages enthält und mit einem weiteren externen Aufruf von .ProcessMessages wird dieser Perfromancevergleich stark verfälscht da dann das 2. .ProcessMessages erstmal darauf wartet das überhaupt Messages im Queue sind. Deine Schleife mit 500*Sleep(10) sollte 5000ms dauern. Da du aber noch zusätzlich Applikation.ProcessMessages aufrufst und damit quasi den Messagequeue unötigt pollst, sollte eigentlich deine Variante wesentlich ungenauer sein. Dh. deine Variante sollte minimal 5 Sekunden + X andauern. Die von mir entwickelte Delay() Funktion gehat da ganz anders vor: 1.) sie benutzt ein "totes" Event das niemals signalisiert und wartet mit einem Timeout auf eingehende Messages. 2.) dadurch wird über OS Funktionen erreicht da der aktuelle Thread komplett schlafen gelegt wird und somit keinerlei Systemperformance unsinnig verbraucht 3.) sollte nun eine Messages schon vor dem Timeout eintreffen kehrt mein Delay() in der innersten Schleife zurück und bearbeitet über .ProcessMessages diese Nachrichten 4.) danach wird die restliche Wartezeit neu berechnet und damit enthält die Gesamtwartezeit dieses Delay() auch die Abarbeitungszeit für .ProcessMessages 5.) das führt dazu das die zusätzliche Zeit die man für .ProcessMessages verbraucht nicht wie bei deinem Delay() oben drauf auf die Gesamtwartezeit addiert wird Ich würde mal davon ausgehen das du einen fehlerhaften Vergleich angestellt hast und dein Application.ProcessMessages nicht auskiommentiert hast wenn du mein Delay() benutzt. Der Vergleich sähe also so aus:
Delphi-Quellcode:
So wäre es ein korrekter und vergleichbarer Test beider Funktionen.
procedure Test;
var I: Integer; T: cardinal; begin T := GetTickCount; for I := 0 to 499 do begin Sleep(10); Application.ProcessMessages; end; T := GetTickCount - T; ShowMessage('Dein Delay = ' + IntToStr(T) + 'ms'); T := GetTickCount; Delay(5000); T := GetTickCount - T; ShowMessage('Mein Delay = ' + IntToStr(T) + 'ms'); end; Übrigens die Abfrage "if Application.Terminated then Exit" hat einen sehr wichtigen Grund. Und Sleep(10) ist im Grunde fast schon witzlos da die Zeitscheiben des Windows OS meisten >= 20ms sind, soll heisen Windows kann sowas garnicht korrekt zeitlich auflösen. Gruß Hagen |
AW: Delay zu langsam?
Wenn ich mich frage warum du überhaupt ein eigens Delay() bauen möchtest dann komme ich zu dem Schluß das es im Grunde nur so sein kann das du noch zusätzlichen Code ausführen möchtest.
Warum nimmst du nicht mein Delay() als Basis und baust das auf deine Bedürfnisse um ? Davon mal abgesehen halte ich generell nichts von einem Delay(). Windows ist Ereignis-basiert und man sollte solche Delays von vornherein vermeiden da sie konzeptionell kontraproduktiv sind. Nun wird man sich fragen warum hat Hagen dann dieses Delay() entwickelt und hier veröffentlicht ? Weil es eine Nachfrage danach gibt und gab und dann sollte man es schon richtig machen. Denn wenn alle Programmierer die Windowssoftware entwickeln mit einfachen Delays auf Basis von Sleep() und Polling arbeiten dann würden wir alle unter einem Windows leiden müssen das noch viel inperformanter wäre als es eh schon ist. Gruß Hagen |
AW: Delay zu langsam?
:thumb:
was würden wir ohne Hagen tun? Perfekt erklärt, und wieder was gelernt. Danke |
AW: Delay zu langsam?
Hallo Hagen,
vielen Dank für Deine ausführlichen Antworten. :) Der Test war an sich schon richtig. Ich habe also natürlich nicht Application.ProcessMessages dringelassen, als Delay(10) aktiv war. Ab Delay(50) ist auch alles in Ordnung. Nur darunter verbrät der Code mit Delay und reichlich Mausbewegung eben deutlich mehr Zeit. Bei Delay(5000) sind es recht genau 5000ms, so wie das sein soll. Mir war nur nicht bewußt, dass es ab der "Schwelle" 50ms zwischen Sleep mit App.Proc. und Delay ohne App.Proc. einen recht deutlichen Unterschied gibt, was das Zeitverhalten angeht. Das hat auch nur den Hintergrund, dass ich im Moment einen Timer brauche, den ich damit getestet habe. Ist mir halt als Nebenprodukt aufgefallen und ich dachte mir, dass ich mal frage. ;) Zitat:
Viele Grüße, Uwe |
AW: Delay zu langsam?
Gerade nochmal so getestet...
Delphi-Quellcode:
procedure TForm1.Test;
var I: Integer; T: cardinal; begin // immer schön mit der Maus rühren... T := GetTickCount; // Schleife dauert ca. 5000ms. for I := 0 to 499 do begin Sleep(10); Application.ProcessMessages; end; T := GetTickCount - T; ShowMessage('Dein Delay = ' + IntToStr(T) + 'ms'); T := GetTickCount; // Schleife dauert 7660ms. for I := 0 to 499 do begin Delay(10); end; T := GetTickCount - T; ShowMessage('Ungeschickter Delay = ' + IntToStr(T) + 'ms'); T := GetTickCount; // Delay(5000) dauert ca. 5000ms. Delay(5000); T := GetTickCount - T; ShowMessage('Sinnvoller Delay = ' + IntToStr(T) + 'ms'); end; |
AW: Delay zu langsam?
Ja das sind so Sachen die passieren wenn man solche Tests auf einem System macht das im Grunde unberechenbar ist. Zb. deine zweite Schleife liefert auf den ersten Blick erstaunliche und wenig verständliche Resultate. Wenn man aber weiß was der Aufruf von Sleep() bedeutet wird das dann klarer. Sleep(0) zb. ist ein sinnvoller Aufruf auch wenn der aktuelle Thread keine 0 Millisekunden schlafen kann. Denn Sleep() führt in jedem Fall einen Task Wechsel aus. Dh. man übergibt programmatisch die Kontrolle an den taskscheduller vom OS und der wird einen Taskwechsel zu einem anderen Task durchführen. Nun entfernst du aber in der 2. Schleife nicht per .ProcessMessages die Nachrichten im Messagequeue und der läuft quasi voll. Das OS scheint nun dafür immer mehr Resourcen zu benötigen, zb. für die Verwaltung dieser Nachrichten und schwups wird der komplette Prozess ausgebremmst.
Durch den Overhead der zusätzlichen API Funktionen in meinem Delay() wird es ebenfalls zu einer Verschlechterung der Perfomance kommen, je kürzer die Wartezeit wird. Zudem ist das Application.ProcessMessages ebenfalls eine "Störquelle". Diese Funktion kehrt abhängig von der Messageabarbeitung zurück. Es könnte also zb. Nachrichten geben die in einer Schleife enden. Zb. viele der Nachrichten für den Non Client Bereich von Fenstern verzweigen intern in Schleifen und geben die Kontrolle an den Aufrufer erst zurück wenn sie es wollen. Klicke und Ziehe zb. mal in den Fensterrahmen und lass die Maustaste gedrückt. Intern wird in eine Schleife verzweigt, im Windowskernel, die erst bei Loslassen der Maus zurückkehrt. Deswegen, und noch vielen anderen Nachteilen, würde ich auf den Aufruf von Applikation.processMesages im eigenen Source oder Komponenten etc.pp. absolut verzichten. Ebenso wie ein Delay() um auf irgendwas zu warten. Das sind alles "Tricks" die kontraproduktiv sind. Kontraproduktiv weil es auch anders und weit besser geht. Zb. einen Timer per Messages oder Callback Funktion zu benutzen oder mit den Events des OS arbeiten, zb. bei fast allen Kommunikationsschnittstellen ist das möglich. Im schlechtesten Fall muß man eben einen Thread abspalten und kann in diesem ohne Messagequeue und Sleep() und Events sicher arbeiten. Man stellt also in jedem Fall das System auf Events um statt zu Pollen. Gruß Hagen |
AW: Delay zu langsam?
Hallo Hagen,
Zitat:
Delphi-Quellcode:
procedure Delay(Milliseconds: integer);
var Tick: DWORD; Event: THandle; begin Event := CreateEvent(nil, False, False, nil); try Tick := GetTickCount + DWORD(Milliseconds); while (Milliseconds > 0) and (MsgWaitForMultipleObjects(1, Event, False, Milliseconds, QS_ALLINPUT) <> WAIT_TIMEOUT) do begin Application.ProcessMessages; // <-- das ist er doch...der Aufruf! if Application.Terminated then Exit; Milliseconds := Tick - GetTickCount; end; finally CloseHandle(Event); end; end; Zitat:
Scheint mir eher so, dass
Delphi-Quellcode:
wesentlich mehr Zeit als Milliseconds verbraucht, wenn die Funktion erstmal ausgeführt wird und Milliseconds < 50ms ist. Da liegt der Hase im Pfeffer und die Beschreibung der API-Funktion sollte vielleicht lauten, dass die Funktion wenigstens 50ms verbrät, wenn eine Message vorliegt.
MsgWaitForMultipleObjects(1, Event, False,
Milliseconds, QS_ALLINPUT Und damit wartet Delay < 50ms eben länger als erwartet, wenn viel los ist. Nicht mehr und nicht weniger. ;) Viele Grüße, Uwe |
AW: Delay zu langsam?
Jo wäre auch eine Erklärung, aber ohne den Source dieser Funktionen ist das Raten ;)
gruß Hagen |
AW: Delay zu langsam?
Na ja, was soll die Funktion schon machen, wenn eine Message vorliegt?
Sie wird sie halt abarbeiten und dabei sicherlich nicht ständig auf die Uhr schauen. :-D Eventuell sollte man bei Milliseconds < 50 einfach den Aufruf von MsgWaitForMultipleObjects weglassen dann würde es immerhin zeitlich bis runter zu 10ms passen. Grüße, Uwe |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:54 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz