Entwickler-Ecke

Algorithmen, Optimierung und Assembler - SLEEP - Vergleichstest


Delete - So 28.10.07 21:29
Titel: SLEEP - Vergleichstest
Ich habe mal SLEEP-Vergleichsmessungen gemacht und war über das Ergebnis doch etwas erstaunt.
Das Resultat ist: Das in DELPHI vorhandene SLEEP() ist unter 30 msec SLEEP(30) unbrauchbar!

Zum Vergleichen habe ich die GetTickCount-Function genutzt - die Genauigkeit ist besser als 2 msec, im Durchschnitt ist die Ungenauigkeit 1 msec, d.h. ab 30 msec sind die Abweichungen unbedeutend.
(Retrieves the number of milliseconds that have elapsed since the system was started, up to 49.7 days.)

Ergebnisse der 10 msec und 20 msec Werte sind offensichtlich rein zufällig und können sogar 30 msec-Werte weit übertreffen! Extremer Fehler, wenn CPU-Load 100% ist.

Neue Version: 20071030


BenBE - So 28.10.07 21:35

Haste ein Sleep auf GetSystemTimeToFileTime-Basis und QPC\QPF-Basis getestet?


Delete - So 28.10.07 21:41

user profile iconBenBE hat folgendes geschrieben:
Haste ein Sleep auf GetSystemTimeToFileTime-Basis und QPC\QPF-Basis getestet?


Nee. Hast Du einen "brauchbaren" Code?

Meinst Du sowas?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure UpdateTimeStamp(H: THandle);
var
  FT: TFileTime;
begin
  GetSystemTimeAsFileTime(FT);
  SetFileTime(H, nilnil, @FT);
end;
//oder sowas:
Begin
    sQpcMinResMls := FormatOutput(Format('%3.9n',[aTimer.getTimeMeasureAs(tsMilliSec,1,tkQPC)]));
    sQpcMinResMic := FormatOutput(Format('%3.9n',[aTimer.getTimeMeasureAs(tsMicroSec,1,tkQPC)]));
    sQpcMinResNan := FormatOutput(Format('%3.9n',[aTimer.getTimeMeasureAs(tsNanosSec,1,tkQPC)]));
    sQpcApiOvdNat := FormatOutput(IntToStr(aTimer.FQpcOverhead)+' units ');
    aReal := aTimer.GetOptimalMeasure(aTimer.FQpcOverhead, tkQPC, aTimeScale);
    sQpcApiOvdOpt := Format('%s (%3.9n)',
                     [TimeScale2Str[Ord(aTimeScale)], aReal ]);
  End;


GTA-Place - So 28.10.07 22:17

Mh...? Sleep ist bei mir genausogut wie PseudoASM und Kernel bei 20ms und gleich wie alle anderen bei 30ms und 10ms. Delay und SysDelay weichen bei 20ms um 15ms von Sleep / PseudoASM / Kernel ab. Bei 100% CPU-Auslastung nicht viel anders.


Delete - So 28.10.07 23:19

Oh Mann, der Tickcountfehler bei 100% Last ist aber hoch mit 15msec!


BenBE - So 28.10.07 23:42

Mit GetSystemTimeAsFileTime erhälst Du nen Zeitstempel ähnlich dem von QPC zurück, den man zur Zeitmessung nutzen kann. Näheres dazu steht im MSDN.


BenBE - Mo 29.10.07 00:28

Hab das Programm mal eben etwas erweitert:

Problem: Du misst falsch!

Hab mal eben noch RDTSCDelay, RDTSCSleep und SystemTimeWait implementiert (oben genannte Messverfahren) und dabei selbst für RDTSC Abweichungen von 15 msec angezeigt bekommen (obwohl RDTSC nachweislich auf meiner CPU mit >500MHz* taktet) ...

Ferner würd ich dich bitten, dein Programm mal bitte etwas aufzuräumen.

Ein weiteres Problem: Zeitkritische Anweisungen werden NICHT korrekt zeitkritisch behandelt. So erzeugst Du durch falsche Handhabung bei der PseudoASM-Variante ein nicht-deterministisches Verhalten, da die Messung außerhalb des ASM-Blocks stattfindet.

*RDTSC auf AMD-CPUs läuft mit CPU-Takt (2GHz bei mir); bei Intels meistens mit 3,6MHz.


Delete - Mo 29.10.07 00:37

RDTSC geht absolut nicht!

@BenBE
Deine Änderungen verschlechtern die Werte.


BenBE - Mo 29.10.07 00:47

Nicht RDTSC geht nicht, sondern die Art, wie Du die Zeiten misst, ist kacke...

Du kannst nicht erwarten, dass wenn Du mit'm Lichtmikroskop ein Atom betrachtest, dass Du dann Quantenfluktuationen bis zur Heisenbergschen Unschärfe nachverfolgen kannst. Genau das machst Du aber, wenn Du mit GetTickCount versuchst, die Qualität von RDTSC zu messen.

Nutze bitte zur Zeitauswertung eine genauere Methode als GetTickCount, dann solltest Du qualitativ bessere Ergebnisse erhalten.


Delete - Mo 29.10.07 00:52

user profile iconBenBE hat folgendes geschrieben:
Nicht RDTSC geht nicht, sondern die Art, wie Du die Zeiten misst, ist kacke...


Ich werde nicht meine CPU auf eine Frequenz festnageln, um RDTSC verwenden zu können - es ist für Zeitmessungen nicht geeignet. BASTA!


GTA-Place - Mo 29.10.07 08:12

user profile iconhathor hat folgendes geschrieben:
Oh Mann, der Tickcountfehler bei 100% Last ist aber hoch mit 15msec!

So wie du den Fehler von GetTickCount bestimmst, kann man den auch nicht bestimmen.


BenBE - Mo 29.10.07 10:45

Stellen wir also fest: Die genutzten Mess-Verfahren sind ungeeignet.

Ich bau's die Tage mal auf QPC\QPF-Messung um.


Delete - Mo 29.10.07 10:59
Titel: Re: SLEEP - Vergleichstest
user profile iconhathor hat folgendes geschrieben:
Ich habe mal SLEEP-Vergleichsmessungen gemacht und war über das Ergebnis doch etwas erstaunt.
Das Resultat ist: Das in DELPHI vorhandene SLEEP() ist unter 30 msec SLEEP(30) unbrauchbar!

Als aller erstes, sollte man sich fragen, was Sleep überhaupt macht:
Zitat:
This function causes a thread to relinquish the remainder of its time slice and become unrunnable for an interval based on the value of dwMilliseconds.

Der Rest der dem Thread zur Verfügung gestellten Zeitscheibe wird also an das System abgegeben und der Thread die angegeben Zeitspanne für nicht zuteilungsfähig erklärt. Da unter Windows NT eine Zeitscheibe ca. 20 Millisekunden lang ist, ist es unmöglich einen Thread für weniger als 20 Millisekunden "schlafen zu legen".


Delete - Mo 29.10.07 17:50
Titel: Gettickcount ist offensichtlich VIEL ungenauer...
Gettickcount ist offensichtlich VIEL ungenauer, als es rechnerisch sein dürfte!
Deshalb habe ich die Zeitmessung geändert:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TForm1.Button47Click(Sender: TObject);
var   a, b, c: Int64; r: real;
begin
  QueryPerformanceFrequency(a);
  QueryPerformanceCounter(b);
  Sleep(1000); //<<<< Die Zeitdauer dieses Befehls wird gemessen
  QueryPerformanceCounter(c);
  r:= (c - b) * 1000 / a;
  Label47.Caption := FloatToStrF(r, ffFixed, 123)+' msec';
end;


Delete - Mo 29.10.07 18:10
Titel: Re: SLEEP - Vergleichstest
user profile iconLuckie hat folgendes geschrieben:

Der Rest der dem Thread zur Verfügung gestellten Zeitscheibe wird also an das System abgegeben und der Thread die angegeben Zeitspanne für nicht zuteilungsfähig erklärt. Da unter Windows NT eine Zeitscheibe ca. 20 Millisekunden lang ist, ist es unmöglich einen Thread für weniger als 20 Millisekunden "schlafen zu legen".


@Luckie

Bei Dir ist ein Update überfällig!
Du hast Dein Wissen offensichtlich aus der WINDOWS95-Ära.
Nichts für ungut: Man sollte immer jede WINDOWS-Version auf den Prüfstand stellen!


BenBE - Mo 29.10.07 20:27

hathor: Hast Du für QPF\QPC_S oder QPF\QPC_D die Messung in dem Screenie drin?

Können ja noch nen Kontest draus machen, wer's mit einfachen Mitteln so genau wie möglich hinbekommt, eine vorgegebene Zeit zu stoppen ...

BTW: SystemTime kann auch im <MSec-Bereich ähnlich wie QPC genutzt werden. Aber wie es die Messungen zeigen: Windows interpoliert dies nicht vollständig. Über einen Registry-Switch (Default 15625uSec) kann man die Update-Zeit einstellen.

Bitte zeige auch für jegliche Varianten die CPU-Auslastung mit an, wenn dies möglich ist ... (Hoffe, du hast da in Bezug auf QPC\QPF_D was festgestellt *G*

Mal sehen, ob hier jemand einen Zusammenhang mit den ersten Screenshots dieses Programms erkennt *G*


Delete - Mo 29.10.07 21:17

Ich habe Folgendes mitgetestet:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
procedure SystemTimeWait(milliseconds: cardinal);
var
    S1: _FILETIME;
    S1V: Int64 absolute S1; //Equal Memory for S1 and S1V; simply an alias definition
    S2: _FILETIME;
    S2V: Int64 absolute S2; //Equal Memory for S2 and S2V; simply an alias definition
Begin
    GetSystemTimeAsFileTime(S1);
    S2V := S1V + milliseconds * 10 * 1000//Convert msec to hnsec

    While S1V < S2V do
        GetSystemTimeAsFileTime(S1);
end;

procedure DelayQPC(t: cardinal);//entspricht Deinem 'procedure RDTSCDelay(milliseconds: cardinal);'
var  a, b: Int64;
Begin
    QueryPerformanceFrequency(b);
    QueryPerformanceCounter(a);
    b := a + (b * t) div 10000;
    While a < b do QueryPerformanceCounter(a);
end;

Die Version mit dem eingebetteten SLEEP habe ich nicht mitgetestet, denn die kann ja nicht besser sein, als der Test Nr.1.
Für die Messungen musste ich den Timer für die CPU-Usage und CPU-Freq. abschalten !!!


GTA-Place - Mo 29.10.07 22:04

Könntest du mal die neue Version oben anhängen?


Delete - Mo 29.10.07 22:22
Titel: Re: SLEEP - Vergleichstest
user profile iconhathor hat folgendes geschrieben:
user profile iconLuckie hat folgendes geschrieben:

Der Rest der dem Thread zur Verfügung gestellten Zeitscheibe wird also an das System abgegeben und der Thread die angegeben Zeitspanne für nicht zuteilungsfähig erklärt. Da unter Windows NT eine Zeitscheibe ca. 20 Millisekunden lang ist, ist es unmöglich einen Thread für weniger als 20 Millisekunden "schlafen zu legen".


@Luckie

Bei Dir ist ein Update überfällig!
Du hast Dein Wissen offensichtlich aus der WINDOWS95-Ära.
Nichts für ungut: Man sollte immer jede WINDOWS-Version auf den Prüfstand stellen!

In wie fern?


BenBE - Di 30.10.07 09:47

Die Version mit dem integriertem Sleep ist in Bezug auf die CPU-Belastung vorteilhafter. Ferner wäre jedoch interessant, wie die Routine be Last-Situationen reagiert. 50ms sind zwar erstmal jenseits von Gut und Böse in Bezug auf die Sleep-Toleranz, bei ungünstiger Zeitscheiben-VErteilung (hohe Auslastung) könnte es aber durchaus zu merklichen ungenauigkeitn kommen.


Delete - Di 30.10.07 10:43

Neue Zeitmessung
CPU-Load, CPU-Freq,
Stress-Funktion eingebaut

Version 20071030 ganz oben.