AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Timer viel zu ungenau?

Ein Thema von Lefko · begonnen am 4. Aug 2003 · letzter Beitrag vom 15. Aug 2003
Antwort Antwort
Seite 2 von 3     12 3      
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#11

Re: Timer viel zu ungenau?

  Alt 8. Aug 2003, 00:12
@MrSpock, am genauesten ist heutzutage der Real Time Counter, RDTSC. Dieser wird mit jedem Takt der externen CPU Taktfrequenz erhöht. Alle neueren CPU's unterstützen diesen:

Delphi-Quellcode:
function IsRDTSCPresent: Boolean; assembler;
// check is CPUID Instruction present
// extracted from my DEC Part I (Copyright), and litte bit expanded to support TSC Flag directly
asm
       PUSHFD
       PUSHFD
       POP EAX
       MOV EDX,EAX
       XOR EAX,0040000h
       PUSH EAX
       POPFD
       PUSHFD
       POP EAX
       XOR EAX,EDX
       JZ @@1
       PUSHFD
       POP EAX
       MOV EDX,EAX
       XOR EAX,0200000h
       PUSH EAX
       POPFD
       PUSHFD
       POP EAX
       XOR EAX,EDX
@@1: POPFD
       TEST EAX,EAX
       JZ @@2
       MOV EAX,1
       DW 0A20Fh // CPUID
       TEST EDX,010h // test RDTSC flag in Features
       SETNZ AL
@@2:
end;

function RDTSC: Int64; // sollte UInt64 sein
asm
       DW 0310Fh // RDTSC Opcode
end;

procedure Test;
var
  Start,Cylces: Int64;
begin
  if IsRDTSCPresent then
  begin
    Start := RDTSC;
    DoAnything;
    Cycles := RDTSC - Start;
  end;
// Cycles ~ Taktzyklen der CPU die DoAnything benötigt hat
end;
Falls dich noch die Umwandlung von Cycles in Millisekunden bzw. Nanosekunden interessiert, poste ich sie.

Gruß Hagen
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#12

Re: Timer viel zu ungenau?

  Alt 8. Aug 2003, 00:17
Her damit. Kommt in die Code-Lib.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Lefko

Registriert seit: 31. Jul 2003
359 Beiträge
 
Delphi 6 Enterprise
 
#13

Noch ne Frage

  Alt 8. Aug 2003, 01:09
Hi,

so, das funktioniert gut mit dem OnlineZeit := now - Startzeit;


ABER: (Es gibt immer ein aber... )
Wie kann ich denn zwei Zeitwerte addieren? der Tageszähler hat ja schon nen wert, auf den die jetzige onlinezeit noch draufaddiert werden soll!

Ich hab das folgendermaßen gemacht, aber es klappt nicht ganz:

Code:
Label9.Caption := Ini.ReadString('Kosten','Tagesz','00:00:00');
Tageszeit := StrToTime(Label9.caption);
Onlinezeit := now - Startzeit;
Label9.caption := TimeToStr(Tageszeit + Onlinezeit);
was is daran falsch?

Edit:
Kommando zurück, hab voll den doofen denkfehler gemacht und zwar an ner ganz anderen stelle..
sorry, alles bestens


MfG Lefko.
Johannes
-=[Nennst du meinen Namen, bin ich schon nicht mehr da]=-
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#14

Re: Timer viel zu ungenau?

  Alt 8. Aug 2003, 02:13
@Lucki, dein Arbeitseifer wird mir langsam unheimlich, ich kann doch nicht jeden meiner Beiträge in die Codelib stellen

Also gut, erstmal der Source, dann die Erklärung:

Delphi-Quellcode:

function RDTSC: Int64; // sollte UInt64 sein
// Liest den Time Stamp Counter der CPU
asm
       DW 0310Fh // RDTSC Opcode, hier als DW für D3-D4
end;

function IsRDTSCPresent: Boolean;
// Überprüft ob der Time Stamp Counter durch die CPU unterstützt wird.
// Extrahiert aus meinem Delphi Encryption Compendium. Es gelten die
// Copyright aus dem DEC, Public Domain.

  function HasRDTSC: Boolean; assembler;
  asm
       PUSH EBX
       PUSHFD
       PUSHFD
       POP EAX
       MOV EDX,EAX
       XOR EAX,0040000h
       PUSH EAX
       POPFD
       PUSHFD
       POP EAX
       XOR EAX,EDX
       JZ @@1
       PUSHFD
       POP EAX
       MOV EDX,EAX
       XOR EAX,0200000h
       PUSH EAX
       POPFD
       PUSHFD
       POP EAX
       XOR EAX,EDX
@@1: POPFD
       TEST EAX,EAX
       JZ @@2
       MOV EAX,1
       DW 0A20Fh // CPUID
       TEST EDX,010h // test RDTSC flag in Features
       SETNZ AL
@@2: POP EBX
  end;

begin
// dieser Try Except Block ist absolut nötig.
// RDTSC kann eine privilegierte Instruktion sein, d.h. das OS kann jederzeit
// so konfiguriert sein das es die CPU anweisst das RDTSC eine priviligierte
// Instruktion ist.
  try
    Result := HasRDTSC;
    if Result then RDTSC;
  except
    Result := False;
  end;
end;

function CalcCPUFrequency(Rounds: Cardinal = 1): Int64;
// Berechnet die CPU Taktfrequenz. Diese Funktion nutzt eine sehr exakte und schnelle Methode.
// Relativ zu einem Referenztakt werden die Taktzyklen der CPU gezählt.
// Danach wird über unseren Referenztakt und dessen Frequenz die Taktzyklen in
// die CPU Taktfrequnz umgerechnet. Die genaueste Referenzquelle im Windows System
// ist QueryPerformaceCounter() + QueryPerformanceFrequncy(). Beide werden durch
// den Real Time Clock Chip der mit dem BIOS zusammenarbeitet erzeugt.
// Auf den meisten Systemen arbeitet dieser mit einem Takt von 3.579.545 Hz = 3.6 MHz.
// D.h. wir können mit dieser Funktion die CPU Taktfrequenz mit einer maximalen
// Genauigkeit von 3.6 MHz errechnen. Sollte die CPU mit 1500MHz getaktet werden so
// beträgt die best mögliche Genauigkeit +- 1500MHz/3.6MHz = +-417 Hz.
// Die Meßmethode selber ist unabhänig vom Tasksheduler von Windows da relativ zu
// zwei Frequenzen die unabhänig von Tasksheduler sind gerechnet wird.
// D.h. die Länge der Meßdauer ist im Grunde unwichtig und kann sehr kurz gehalten werden.

// Warum beschreibe ich das ??
// Weil es im WEB viele Sourcen gibt die eine Meßschleife per Sleep() oder GetTickCount()
// aufbauen. Beide Methoden sind abhängig vom Tasksheduler und haben eine viel zu geringe
// Genauigkeit. Die bestmögliche Genauigkeit mit GetTickCount() und einer 1.5GHz CPU
// liegt bei 1.500.000.000Hz / 1.000Hz = +-1.500.000 = +-1.5MHz. D.h. die Auflösung
// mit GetTickCount = 1ms = 1000Hz ist 1.500.000 / 417 = 3.597 mal schlechter als
// mit nachfolgender Methode. Die Auflösung bei Sleep() liegt bestenfalls bei 10ms,
// also 10 mal schlechter als mit GetTickCount().

// Natürlich wird die theoretische Genauigkeit bei einer 1.5GHz CPU von +-417Hz nicht
// erreicht. Im Durchschnitt liegt sie jedoch bei +- 2000Hz.
// Rounds erhöht die Genauigkeit, 100 macht es ~100 mal genauer, aber nur hypothetisch.

// Probleme könnten mit den Int64 auftreten falls die CPU schon sehr lange läuft.
var
  C,F,S,E,D,T: Int64;
begin
  if IsRDTSCPresent and QueryPerformanceFrequency(F) and QueryPerformanceCounter(S) then
  begin
    C := F * (Rounds +1);
    QueryPerformanceCounter(S);
    D := RDTSC;
    while C > 0 do Dec(C);
    QueryPerformanceCounter(E);
    T := RDTSC;
    Result := Round((T - D) * F / (E - S));
  end else Result := 0;
end;

var
  Frequency: Int64 = 0;

function CPUFrequency: Int64;
// gibt die Takzyklen pro Sekunde zurück
begin
  if Frequency = 0 then
  begin
    Frequency := CalcCPUFrequency;
    if Frequency = 0 then
      raise Exception.Create('Kann CPU Frequenz nicht berechnen');
  end;
  Result := Frequency;
end;

function Secs(Cycles: Int64): Double;
// rechnet Taktzyklen in Sekunden um
begin
  Result := Cycles / CPUFrequency;
end;

function Ticks(Cycles: Int64): Double;
// rechnet Taktzyklen in Millisekunden um
begin
  Result := Cycles * 1000 / CPUFrequency;
end;

procedure Test;
var
  Start,Stop: Int64;
  Tick: DWord;
begin
  WriteLn('CPU Taktfrequenz ist ', CPUFrequency/1000000.0:6:1, ' MHz');
  
  Tick := GetTickCount + 100;

  Start := RDTSC;
  while GetTickCount < Tick do ;
  Stop := RDTSC;

  WriteLn;
  WriteLn('Testschleife dauerte: ');
  WriteLn('Taktzyklen : ', Stop - Start:10);
  WriteLn('Millisekunden : ', Ticks(Stop - Start):10:2);
  WriteLn('Sekunden : ', Secs(Stop - Start):10:2);

end;

// diese Source ist Public Domain, Hagen Reddmann at Negah
Ok, IsRDTSCPresent erkärt sich von selber, es testet ob die CPU alle nötigen Features unterstützt und ob das OS diese auch für uns "freigeschaltet" hat.

Am wichtigsten ist die Ermittlung wieviele Takte pro Sekunde nun die CPU ausführen kann, d.h. die Taktfrequenz der CPU wird benötigt.
Mit CalcCPUFrequency wird dies erledigt. Um ein maximal exaktes und schnelles Ergebnis zu bekommen ist es wichtig mit welcher Referenzquelle man arbeitet. Auf Windows-Systemen ohne spezielle Hardware stehen uns GetTickCount(), Sleep() und QueryPerformanceFrequency() zur Verfügung. Sleep() arbeitet im zehntel Millisekunden Bereich genau, und stellt eigentlich keine Referenzquelle dar da deren Frequenz eben ungleichmäßig ist (das Multithreading funkt dazwichen). GetTickCount arbeitet auf 1ms genau, deren Taktfrequenz ist 1000 Hz. Am genauesten ist QueryPerformanceFrequency() die abhänig vom System ca. 3.6 MHz ist. Diese arbeitet mit dem RTC (Real Time Clock Chip) der teilweise zur Hardwareausstattung eines BIOS gehört.

Wir ermitteln unsere Referenzfrequenz in F. Unsere Meßschleife sollte nun länger als 3.6 MHz Taktzyklen dauern um keine groben Meßfehler durch Interferenzbildung bei zu geringer Abtastrate zu erhalten.
Wir ermitteln einmal in S den Startzeitpunkt mit QueryPerformanceCounter() und in D in Taktzyklen. Nun warten wir eine weile und ermitteln unsere Stopzähler in E und T. Um nun die Taktfrequnz zu ermitteln führen wir eine einfache Verhältnisgleichung durch, wie im Matheunterricht gelernt (tja hat doch was gebracht

Also E - S = Dauer in QueryPerformanceCounter, T - D = Dauer in Taktzyklen, somit (T - D) * F / (E - S) = Taktfrequenz.

Gruß Hagen
Angehängte Dateien
Dateityp: exe cpuinfo.exe (31,0 KB, 54x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#15

Re: Timer viel zu ungenau?

  Alt 8. Aug 2003, 04:09
Zitat von negaH:
@Lucki, dein Arbeitseifer wird mir langsam unheimlich,
Die Arbeit machst ja du, nicht ich. Aber wenn du meinst, ich mache sie, dann sind meine Suggestivkräfte besser als ich dachte.
Zitat:
ich kann doch nicht jeden meiner Beiträge in die Codelib stellen
Bisher war es aber mindestens jeder zweite Wert dort aufgenommen zu werden.

Öhm, ich mußte in den Projektoptionen "Zuweisbare typisierte Konstanten" aktivieren, sonst gab es hier:
Frequency := CalcCPUFrequency; einen Fehler: Rechter Seite kann nicht zugewiesen werden. Kann man das noch ausbügeln?
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#16

Re: Timer viel zu ungenau?

  Alt 8. Aug 2003, 10:15
Jo schade, hier kommen wir an einen Punkt der mich am neuen Delphi Compiler ärgert. Ich finde die Deklaration von privaten aber globalen initialisierten Variablen echt sauber, denn Frequency soll global sein, aber nur lokal in CPUFrequency sichtbar.
Man kann zwar im D7 einen Compilerswitch stetzen (WRITABLECONST ON} oder so, aber ich habe nun Frequency als Unit Lokale Var deklariert. Damit wird sie eben auch global zu allen nachfolgenden Unit Proceduren, aber was soll's.


Gruß Hagen
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#17

Re: Timer viel zu ungenau?

  Alt 8. Aug 2003, 10:25
Da wünscht man sich dann eine static Deklaration, wie unter C++.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#18

Re: Timer viel zu ungenau?

  Alt 8. Aug 2003, 10:38
Richtig, und die lokale initialisierte Const Deklaration ist für mich dieser static !
Man sucht sich ja doch aus allen Sprachen die man so beherrscht die besten Features raus
Ein
Delphi-Quellcode:
procedure XYZ;
var
  MyStatic: Integer = 0;
begin
end;
Würde mir als Ersatz schon reichen.

Gruß Hagen
  Mit Zitat antworten Zitat
Lefko

Registriert seit: 31. Jul 2003
359 Beiträge
 
Delphi 6 Enterprise
 
#19

Re: Timer viel zu ungenau?

  Alt 14. Aug 2003, 21:58
Wie war das? Neues Thema, neuer Thread?

MfG Lefko
Johannes
-=[Nennst du meinen Namen, bin ich schon nicht mehr da]=-
  Mit Zitat antworten Zitat
Lefko

Registriert seit: 31. Jul 2003
359 Beiträge
 
Delphi 6 Enterprise
 
#20

Re: Timer viel zu ungenau?

  Alt 14. Aug 2003, 22:34
Ich hab da jetzt aber doch noch ein problem:

ich hab ein label, in dem die schon vorhandenen kosten stehen, etwa so: "4,5215€ diesen Monat".
Nun soll immer, wenn die onlinezeit (---> onlinezeit := now - startzeit) um eine minute erhöht wird, der satz ( € / Min) addiert werden; WIE SOLL DAS GEHEN? Ich komme auf keinen richtigen ansatz, nur mist!

ich könnte eine variable "minuten" haben, die dann bei starttime auf 0 gesetzt wird und dann prüft der timer immer, ob "onlineminuten" (mit decodetime(onlinzeit,...) = "minuten" ist; wenn nicht, dann wird minuten := onlineminuten gesetzt und der satz zu dem label addiert.

mensch, wärend ich den post schreibe fällt mir ne möglichkeit ein, lol

öh, hat jemand ne bessere idee?

MfG Lefko.
Johannes
-=[Nennst du meinen Namen, bin ich schon nicht mehr da]=-
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:28 Uhr.
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