Hallo,
ich versuche gerade, etwas Delphi nach C unter Linux zu transportieren.
Zur Zeitmessung^WZeitschätzung wird unter Delphi auf den Zyklusticker des Pentium zugegriffen: <code> var: Start,Now,SystemFrequency: Int64;
queryPerformanceFrequency( SystemFrequency ); queryPerformanceCounter(Start);
queryPerformanceCounter(Now); Time := (Now-Start)/ SystemFrequency)*1000; // in ms </code>
queryPerformanceCounter() hat sein Pendant (im Falle eines Pentium) sicher in <code src="asm/msr.h"> #define rdtscll(val) \ __asm__ __volatile__("rdtsc" : "=A" (val)) </code> Aber queryPerformanceFrequency()? <code>grep "cpu MHz" /proc/cpuinfo</code> kann es ja wohl nicht sein. Kann da jemand einen Tip geben?
Aber noch was anderes: Habe ich richtig gerechnet, daß Now-Start wohl bei normaler Nutzung auf einem Arbeitsplatzrechner niemals einen Fehler erzeugen kann? 2^63-1 / 2*10^9 (GHz) / 60 (sec) / 60 (min) / 24 (h) = 53375.99558365032 (Tage)?
Gruß Friedrich
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Hi, Am Freitag, 10. September 2004 19:40 schrieb Friedrich W. H. Kossebau:
Hallo,
ich versuche gerade, etwas Delphi nach C unter Linux zu transportieren.
Zur Zeitmessung^WZeitschätzung wird unter Delphi auf den Zyklusticker des Pentium zugegriffen:
<code> var: Start,Now,SystemFrequency: Int64;
queryPerformanceFrequency( SystemFrequency ); queryPerformanceCounter(Start);
queryPerformanceCounter(Now); Time := (Now-Start)/ SystemFrequency)*1000; // in ms
</code>
queryPerformanceCounter() hat sein Pendant (im Falle eines Pentium) sicher in <code src="asm/msr.h"> #define rdtscll(val) \ __asm__ __volatile__("rdtsc" : "=A" (val))
</code> Aber queryPerformanceFrequency()? <code>grep "cpu MHz" /proc/cpuinfo</code> kann es ja wohl nicht sein. Kann da jemand einen Tip geben?
Wenn ich das richtig verstehe, willst du die Taktfrewuenz in MHz berechnen. Folgender Code sollte es können:
include <stdio.h> #include <unistd.h>
#define rdtsc(X) asm volatile("rdtsc":"=A" (X))
int main(void) { long long start, stop, takte, speed;
rdtsc(start); sleep(10); rdtsc(stop); takte=stop-start; speed=takte/10000000; printf("Tasktgeschwindigkeit: %d MHz.\n",speed);
}
Wobei die 10 sekündische Wartezeit (sleep(10);) wohl etwas sehr lang ist. ;-) Prinzipiell zählt das Programm einfach die Takte, die die Befehle zwischen rdtsc(start); und rdtsc(stop); brauchen.
Ciao, Martin
- -- | Martin Eisfeld martin.eisfeld@gmx.net | GnuPG-KeyID: 0x70AC13D5 | Homepage: http://www.martin-eisfeld.de/ | LinuxInfoTag Dresden * 30.10.2004 * http://www.linuxinfotag.de/
Am Freitag, 10. September 2004 19:53 schrieb Martin Eisfeld:
Am Freitag, 10. September 2004 19:40 schrieb Friedrich W. H. Kossebau:
ich versuche gerade, etwas Delphi nach C unter Linux zu transportieren.
Zur Zeitmessung^WZeitschätzung wird unter Delphi auf den Zyklusticker des Pentium zugegriffen:
<code> var: Start,Now,SystemFrequency: Int64;
queryPerformanceFrequency( SystemFrequency ); queryPerformanceCounter(Start);
queryPerformanceCounter(Now); Time := (Now-Start)/ SystemFrequency)*1000; // in ms
</code>
queryPerformanceCounter() hat sein Pendant (im Falle eines Pentium) sicher in <code src="asm/msr.h"> #define rdtscll(val) \ __asm__ __volatile__("rdtsc" : "=A" (val))
</code> Aber queryPerformanceFrequency()? <code>grep "cpu MHz" /proc/cpuinfo</code> kann es ja wohl nicht sein. Kann da jemand einen Tip geben?
Wenn ich das richtig verstehe, willst du die Taktfrewuenz in MHz berechnen.
Nein, ich will möglichst genau (deswegen der CPU-Zyklus) die Dauer zwischen zwei Codestellen berechnen (das Multitasking und alle ISR zwischendurch mal ignorierend).
Folgender Code sollte es können:
include <stdio.h> #include <unistd.h>
#define rdtsc(X) asm volatile("rdtsc":"=A" (X))
int main(void) { long long start, stop, takte, speed;
rdtsc(start); sleep(10);
sleep( 10 ) bedeutet leider nicht, daß er genau (auf die Millisekunde) 10 Sekunden schläft. Ich habe nicht nachgeschaut, aber sleep kommuniziert bestimmt mit dem Scheduler, und dessen Auflösung ist, uhm, war IIRC mal 10 ms, keine Ahnung, was der aktuell (Kernel 2.4/2.6) ist.
rdtsc(stop); takte=stop-start; speed=takte/10000000; printf("Tasktgeschwindigkeit: %d MHz.\n",speed);
Näherungswert :)
Aber danke Friedrich
On Fri, Sep 10, 2004 at 07:53:23PM +0200, Martin Eisfeld wrote:
Wenn ich das richtig verstehe, willst du die Taktfrewuenz in MHz berechnen. Folgender Code sollte es können:
include <stdio.h> #include <unistd.h>
#define rdtsc(X) asm volatile("rdtsc":"=A" (X))
wenn das das macht, was ich vermute, nämlich den TSC (time stamp counter) auslesen, dann geht das nur auf Prozessoren, die keine fortgeschrittenen Energiesparfunktionen (C3-States) kennen. Geht der Prozessor in C3, bleibt der TSC stehen.
Darum passiert hier z.B. folgendes: strolchi:~ # cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq 600000 strolchi:~ # powersave -r 235.674072 MHz strolchi:~ # cat /proc/acpi/processor/CPU0/power active state: C3 default state: C1 bus master activity: 00001000 states: C1: promotion[C2] demotion[--] latency[000] usage[12856040] C2: promotion[C3] demotion[C1] latency[050] usage[109088795] *C3: promotion[--] demotion[C2] latency[050] usage[97276603]
powersave -r macht ziemlich genau dasselbe, wie dein Programm.
Nun kann man das benutzen der C3-States zwar leider ziemlich einfach verhindern (einfach z.B. die Soundkarte benutzen oder eine USB-Maus anstecken), aber insgesamt bleibt es ein Hack.
Wobei die 10 sekündische Wartezeit (sleep(10);) wohl etwas sehr lang ist. ;-) Prinzipiell zählt das Programm einfach die Takte, die die Befehle zwischen rdtsc(start); und rdtsc(stop); brauchen.
Wie geschrieben, der TSC kann stehenbleiben.
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Friday 10 September 2004 vb m,.- 7:40 pm /, Friedrich W. H. Kossebau wrote:
Hallo,
ich versuche gerade, etwas Delphi nach C unter Linux zu transportieren.
<code> var: Start,Now,SystemFrequency: Int64;
queryPerformanceFrequency( SystemFrequency ); queryPerformanceCounter(Start);
queryPerformanceCounter(Now); Time := (Now-Start)/ SystemFrequency)*1000; // in ms
</code>
Wenn ich das richtig deute, ist das wohl so eine Art Zeitmessung über Ausführungszeit eines Programms.
- --> man 1 time und dann über die Sourcen weiter. (Sollte dort auch system- und prozessorunabhängig gelöst sein..)
Bernhard
Am Freitag, 10. September 2004 20:24 schrieb Bernhard Schiffner:
On Friday 10 September 2004 vb m,.- 7:40 pm /, Friedrich W. H. Kossebau
wrote:
Hallo,
ich versuche gerade, etwas Delphi nach C unter Linux zu transportieren.
<code> var: Start,Now,SystemFrequency: Int64;
queryPerformanceFrequency( SystemFrequency ); queryPerformanceCounter(Start);
queryPerformanceCounter(Now); Time := (Now-Start)/ SystemFrequency)*1000; // in ms
</code>
Wenn ich das richtig deute, ist das wohl so eine Art Zeitmessung über Ausführungszeit eines Programms.
Yup. Wenn auch innerhalb eines Programms, nicht des ganzen.
--> man 1 time und dann über die Sourcen weiter. (Sollte dort auch system- und prozessorunabhängig gelöst sein..)
Aha, danke. (Wenn ich nun nur die Sourcen gerade zur Hand hätte, seufz...) Muß ich nur hoffen, daß die auch den richtigen Timer nutzen und nicht nur den normalem Clock-Timer...
Gruß Friedrich
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Friday 10 September 2004 vb m,.- 7:40 pm /, Friedrich W. H. Kossebau wrote:
Hallo,
ich versuche gerade, etwas Delphi nach C unter Linux zu transportieren.
Zur Zeitmessung^WZeitschätzung wird unter Delphi auf den Zyklusticker des Pentium zugegriffen:
...
Gruß Friedrich
man 1 time hilft bei der Auflösung von Zeiten in der Zuordnung ( Kernel / User )
man 2 gettimeofday ist ein guter Wrapper um den ganzen __asm__ -Kram und macht reine, zuverlässige und saubere Zeitmessung
Bernhard
Am Fr, den 10.09.2004 schrieb Friedrich W. H. Kossebau um 19:40:
queryPerformanceCounter(Start);
queryPerformanceCounter(Now);
[...]
Aber noch was anderes: Habe ich richtig gerechnet, daß Now-Start wohl bei normaler Nutzung auf einem Arbeitsplatzrechner niemals einen Fehler erzeugen kann? 2^63-1 / 2*10^9 (GHz) / 60 (sec) / 60 (min) / 24 (h) = 53375.99558365032 (Tage)?
Theoretisch ja.
Aber: QueryPerformanceCounter() ist Win32-API und deren Dokumentation sagt, daß es ab und zu bei SMP vorkommt, daß die Counter der vorhandenen CPUs nicht synchron laufen, obwohl sie es eigentlich sollten. Wenn Du für den Thread keine CPU-Affinität festgelegt hast, kannst Du also ein Problem bekommen. Außerdem bin ich mir nicht zu 100% sicher, daß der Counter bei 0 losläuft. Außerdem2: Die Auflösung des Counters ist nicht 1, d.h. Now-Start kann durchaus auch mal 0 sein.
Eric p.s. Ist es eigentlich schlecht fürs Linux-Karma, wenn man solches Detailwissen offenbart?
Am Samstag, 11. September 2004 18:44 schrieb Eric Schaefer:
Am Fr, den 10.09.2004 schrieb Friedrich W. H. Kossebau um 19:40:
queryPerformanceCounter(Start);
queryPerformanceCounter(Now);
[...]
Aber noch was anderes: Habe ich richtig gerechnet, daß Now-Start wohl bei normaler Nutzung auf einem Arbeitsplatzrechner niemals einen Fehler erzeugen kann? 2^63-1 / 2*10^9 (GHz) / 60 (sec) / 60 (min) / 24 (h) = 53375.99558365032 (Tage)?
Theoretisch ja.
Aber: QueryPerformanceCounter() ist Win32-API und deren Dokumentation sagt, daß es ab und zu bei SMP vorkommt, daß die Counter der vorhandenen CPUs nicht synchron laufen, obwohl sie es eigentlich sollten. Wenn Du für den Thread keine CPU-Affinität festgelegt hast, kannst Du also ein Problem bekommen.
Oha. Na gut, muß man halt bedenken.
Außerdem bin ich mir nicht zu 100% sicher, daß der Counter bei 0 losläuft.
Der Grund dafür würde mich dann aber interessieren. Immerhin hat keiner der Code-Beispiele, die ich im Netz fand, den Fall betrachtet, daß der Counter überläuft und zurücksetzt. Oder waren die alle unfähig?
Außerdem2: Die Auflösung des Counters ist nicht 1, d.h. Now-Start kann durchaus auch mal 0 sein.
? Quelle? Wieso nicht? Der muß doch nur im Takt hochzählen?
Eric p.s. Ist es eigentlich schlecht fürs Linux-Karma, wenn man solches Detailwissen offenbart?
Wie ist das jetzt gemeint?
Danke für die Hinweise
Gruß Friedrich
Am Sa, den 11.09.2004 schrieb Friedrich W. H. Kossebau um 18:15:
Außerdem bin ich mir nicht zu 100% sicher, daß der Counter bei 0 losläuft.
Der Grund dafür würde mich dann aber interessieren. Immerhin hat keiner der Code-Beispiele, die ich im Netz fand, den Fall betrachtet, daß der Counter überläuft und zurücksetzt. Oder waren die alle unfähig?
Ist das eine Trickfrage? ;-) Wo ist definiert, daß der Counter beim Rechnerstart bei 0 losläuft?
Außerdem2: Die Auflösung des Counters ist nicht 1, d.h. Now-Start kann durchaus auch mal 0 sein.
? Quelle? Wieso nicht? Der muß doch nur im Takt hochzählen?
Theoretisch schon, aber QueryPerformanceCounter() liefert nur Werte mit einer Granularität > 1. Quelle: Empirisch ermittelt.
p.s. Ist es eigentlich schlecht fürs Linux-Karma, wenn man solches Detailwissen offenbart?
Wie ist das jetzt gemeint?
Sinkt mein Linux-Karma, wenn ich solch intimes Win32-API-Wissen offenbare?
Eric
Am Samstag, 11. September 2004 20:39 schrieb Eric Schaefer:
Am Sa, den 11.09.2004 schrieb Friedrich W. H. Kossebau um 18:15:
Außerdem bin ich mir nicht zu 100% sicher, daß der Counter bei 0 losläuft.
Der Grund dafür würde mich dann aber interessieren. Immerhin hat keiner der Code-Beispiele, die ich im Netz fand, den Fall betrachtet, daß der Counter überläuft und zurücksetzt. Oder waren die alle unfähig?
Ist das eine Trickfrage? ;-) Wo ist definiert, daß der Counter beim Rechnerstart bei 0 losläuft?
Reine Vermutung von mir. Ich wüßte es auch gern genau.
Außerdem2: Die Auflösung des Counters ist nicht 1, d.h. Now-Start kann durchaus auch mal 0 sein.
? Quelle? Wieso nicht? Der muß doch nur im Takt hochzählen?
Theoretisch schon, aber QueryPerformanceCounter() liefert nur Werte mit einer Granularität > 1. Quelle: Empirisch ermittelt.
Wie genau ermittelt? Zwei 1-Zyklische Maschinenbefehle direkt hintereinander, die den Wert in jeweils ein anderes Register kopieren, und dann die Differenz gebildet? QueryPerformanceCounter() ist vielleicht so implementiert, daß es mehrere CPU-Zyklen braucht...
p.s. Ist es eigentlich schlecht fürs Linux-Karma, wenn man solches Detailwissen offenbart?
Wie ist das jetzt gemeint?
Sinkt mein Linux-Karma, wenn ich solch intimes Win32-API-Wissen offenbare?
Naja, steigt in meinen Augen eher, denn Du kannst vergleichen und hast trotzdem Deine Meinung ;)
Gruß Friedrich
On Sun, 2004-09-12 at 10:45, Friedrich W. H. Kossebau wrote:
Wo ist definiert, daß der Counter beim Rechnerstart bei 0 losläuft?
Reine Vermutung von mir. Ich wüßte es auch gern genau.
Nix genaues weiß man nicht...
Theoretisch schon, aber QueryPerformanceCounter() liefert nur Werte mit einer Granularität > 1. Quelle: Empirisch ermittelt.
Wie genau ermittelt? Zwei 1-Zyklische Maschinenbefehle direkt hintereinander, die den Wert in jeweils ein anderes Register kopieren, und dann die Differenz gebildet? QueryPerformanceCounter() ist vielleicht so implementiert, daß es mehrere CPU-Zyklen braucht...
Nein, das meinte ich nicht.
DWORD x,y; QueryPerformanceCounter(&x); DoSomething(); QueryPerformanceCounter(&y);
y-x kann dabei durchaus 0 sein. Es ist aber schon lange her, daß ich das getestet habe. D.h. es kann mittlerweile so sein, daß y-x immer >0 ist. In diesem Fall widerrufe ich und behaupte das Gegenteil ;-)
Naja, steigt in meinen Augen eher, denn Du kannst vergleichen und hast trotzdem Deine Meinung ;)
Das war auch nicht so ganz ernst gemeint. Generell finde ich die API nicht so furchtbar schlecht, wie sie immer gemacht wird. Es ist auch eigentlich alles irgendwie dokumentiert, aber es ist häufig nicht ganz einfach die richtige Doku zu finden. Aber da geht es Linux ja auch nicht anders.
Um mal wieder OnT zu werden: Manchmal ist es ein echtes Problem zu einem Thema die Doku zu finden, da man einfach nicht weiß, daß gewisse Schnittstellen überhaupt vorhanden sind. Es müßte für Linux eine Dokumentation geben die nicht irgendwelche APIs dokumentiert (davon gibt es nun wirklich reichlich), sondern statt dessen Konzepte. So in der Art wie: "Wie messe ich möglichst genau, wieviel Zeit zwischen A und B vergangen ist?", "Was muß ich tun, damit mein Programm als Daemon laufen kann?". Vielfach findet man sowas in guten Büchern (APITUE), aber eben auch nicht immer alle Themen. Oft bekommt man auch nur ein Interface hingeklatscht, ohne weitere Erklärung, warum man das so und nicht anders machen muß und was evtl. der Unterschied zu anderen verbreiteten Interfaces ist (siehe man). Als Quickreferenz ist das je auch wirklich toll, aber daneben fehlt eben die Dokumentation des Konzeptes.
Eric
Am Sonntag, 12. September 2004 14:04 schrieb Eric Schaefer:
On Sun, 2004-09-12 at 10:45, Friedrich W. H. Kossebau wrote:
Theoretisch schon, aber QueryPerformanceCounter() liefert nur Werte mit einer Granularität > 1. Quelle: Empirisch ermittelt.
Wie genau ermittelt? Zwei 1-Zyklische Maschinenbefehle direkt hintereinander, die den Wert in jeweils ein anderes Register kopieren, und dann die Differenz gebildet? QueryPerformanceCounter() ist vielleicht so implementiert, daß es mehrere CPU-Zyklen braucht...
Nein, das meinte ich nicht.
DWORD x,y; QueryPerformanceCounter(&x); DoSomething(); QueryPerformanceCounter(&y);
y-x kann dabei durchaus 0 sein. Es ist aber schon lange her, daß ich das getestet habe. D.h. es kann mittlerweile so sein, daß y-x immer >0 ist. In diesem Fall widerrufe ich und behaupte das Gegenteil ;-)
Mh, vielleicht war ja QueryPerformanceCounter() bogus implementiert, sprich, der Wert des Hochzählregisters nicht als mutual markiert und der Compiler hat die zweite Abfrage wegoptimiert ;) Hat jemand den Sourcecode? :P
Naja, steigt in meinen Augen eher, denn Du kannst vergleichen und hast trotzdem Deine Meinung ;)
Das war auch nicht so ganz ernst gemeint. Generell finde ich die API nicht so furchtbar schlecht, wie sie immer gemacht wird. Es ist auch eigentlich alles irgendwie dokumentiert, aber es ist häufig nicht ganz einfach die richtige Doku zu finden. Aber da geht es Linux ja auch nicht anders.
Um mal wieder OnT zu werden: Manchmal ist es ein echtes Problem zu einem Thema die Doku zu finden, da man einfach nicht weiß, daß gewisse Schnittstellen überhaupt vorhanden sind. Es müßte für Linux eine Dokumentation geben die nicht irgendwelche APIs dokumentiert (davon gibt es nun wirklich reichlich), sondern statt dessen Konzepte. So in der Art wie: "Wie messe ich möglichst genau, wieviel Zeit zwischen A und B vergangen ist?", "Was muß ich tun, damit mein Programm als Daemon laufen kann?". Vielfach findet man sowas in guten Büchern (APITUE), aber eben auch nicht immer alle Themen. Oft bekommt man auch nur ein Interface hingeklatscht, ohne weitere Erklärung, warum man das so und nicht anders machen muß und was evtl. der Unterschied zu anderen verbreiteten Interfaces ist (siehe man). Als Quickreferenz ist das je auch wirklich toll, aber daneben fehlt eben die Dokumentation des Konzeptes.
Dafür gibt es doch die HowTos, auch wenn ich zugebe, daß die nicht alles abdecken, viel zu oft veraltet oder lückenhaft sind. Vielleicht schafft es das Linux-Documentation-Project (http://www.tldp.org) ja, das irgendwann zu verändern.
Gruß Friedrich
Friedrich W. H. Kossebau wrote:
Am Samstag, 11. September 2004 20:39 schrieb Eric Schaefer:
Ist das eine Trickfrage? ;-) Wo ist definiert, daß der Counter beim Rechnerstart bei 0 losläuft?
Reine Vermutung von mir. Ich wüßte es auch gern genau.
Wenn Du Geschwindigkeit und Wert des Counters hast, dann sollte es doch nicht das Problem sein die Zeit zu errechnen. Ich denk mal man sollte nach einem Kaltstart möglichst wenig Zeit im Bootmanager verbringen und dann bei frisch-hochgefahrenem System den Wert mit der Ausgabe von uptime(1) (procinfo(8) bringts noch genauer) vergleichen.
Versuchs doch mal wenn Du Lust und Laune hast, würde mich auch interessieren.
Stephan.
lug-dd@mailman.schlittermann.de