Seite 1 von 2
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 20:40
von dot
Hast du vielleicht im Hintergrund was laufen, das den PCI Express Bus in irgendeiner Form belastet (Disk Access etc.)?
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 20:43
von Niki
dot hat geschrieben:Hast du vielleicht im Hintergrund was laufen, das den PCI Express Bus in irgendeiner Form belastet (Disk Access etc.)?
Ich habe noch mal alles dicht gemacht, inklusive Virenscanner und Co, aber das Ergebnis ist nicht besser geworden.
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 20:46
von dot
Hm, wär mal interessant inwiefern die Rückgabewerte von DateTime.Now mit der Stopwatch übereinstimmen...
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 21:03
von Niki
dot hat geschrieben:Hm, wär mal interessant inwiefern die Rückgabewerte von DateTime.Now mit der Stopwatch übereinstimmen...
Stopwatch ist hier genau so, allerdings benutze ich das Ding zu ersten Mal und vielleicht falsch.
Code: Alles auswählen
Stopwatch sw = new Stopwatch();
sw.Start();
double start = sw.ElapsedMilliseconds;
while (sw.ElapsedMilliseconds < 1000.0)
{
double cur = sw.ElapsedMilliseconds;
Console.WriteLine(cur - start + " - " + cur);
}
sw.Stop();
QPC gibt mit delta-Zeiten kleiner 1*10^(-6) Sekunden, also wesentlich genauer.
Ob das aber alles so messbar ist, das ist fraglich, da ich nicht weiß ob Console.WriteLine das alles komplett verfälscht.
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 21:15
von Artificial Mind
Also ich kann ca. 20 mal Pro Millisekunde Console.WriteLine ausführen, daher wird keine Verfälschung kommen bei dem Millisekundentest.
Aber echt interessant dass du anscheinend 15ms bekommst, was für ein System hast du denn?
QPC sollte halt die genauste dem System verfügbare Zeit zurückgeben, deswegen ist das Ergebnis soweit zu erwarten, allerdings sollte man da wirklich nicht mehr Console.WriteLine nutzen ;)
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 21:20
von Niki
Ok, mein Stopwatch Code ist voll daneben. Hab's nochmal richtig gemacht, und das Ergebnis ist vergleichbar mit QPC:
Code: Alles auswählen
Stopwatch sw = new Stopwatch();
sw.Start();
long start = sw.ElapsedTicks;
long last = start;
while (sw.ElapsedTicks < Stopwatch.Frequency)
{
long cur = sw.ElapsedTicks;
double delta = (double)(cur - last) / (double)Stopwatch.Frequency;
last = cur;
Console.WriteLine(String.Format("{0:0.0000000}", delta));
}
sw.Stop();
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 21:22
von Krishty
Ich habe den Thread nicht verfolgt, will aber einwerfen, dass die Timer-Auflösung von Windows standardmäßig 15,6 ms beträgt.
Sobald eine Anwendung auf dem System timeBeginPeriod(1) ausgeführt hat (meistens Multimedia-Anwendungen, aber früher auch Flash oder Chrome; Firefox afaik heute noch) ist die Genauigkeit auf 1 ms erhöht. Das bleibt so lange bestehen bis eine Anwendung einen anderen Wert schreibt oder das System neu gestartet wird. Man sollte also nur mit einem frisch neu gestarteten System testen.
Okay; hat sich wohl gerade erledigt.
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 21:24
von Niki
Artificial Mind hat geschrieben:Aber echt interessant dass du anscheinend 15ms bekommst, was für ein System hast du denn?
64 Bit Core i7 920 @ 2.67 GHz. Das Motherboard ist glaube ich ein Asus PT6 Deluxe, aber da müsste ich jetzt booten um sicher zu sein. Das Ding ist schon recht alt, aber das ist noch Qualität die nicht tot zu kriegen ist.
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 21:27
von Artificial Mind
Ok das mit timeBeginPeriod ist echt interessant. Globale Nebeneffekte ftw.
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 21:40
von dot
An timeBeginPeriod() hatte ich auch schon gedacht, das kann man aber ausschließen, da DateTime.Now bei mir 1ms Auflösung besitzt, während der TickCount die gewohnten 15ms zeigt...
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 21:58
von Niki
Mein Code zum Setzen der Thread-Affinität sieht nun wie folgt aus:
Code: Alles auswählen
Thread.BeginThreadAffinity();
Process process = Process.GetCurrentProcess();
ProcessThreadCollection threadCollection = process.Threads;
uint currentThreadId = (uint)NativeWin32.GetCurrentThreadId();
foreach (ProcessThread processThread in threadCollection)
{
if ((uint)processThread.Id == currentThreadId)
{
processThread.IdealProcessor = 0;
processThread.ProcessorAffinity = (IntPtr)1;
break;
}
}
// ... yada yada, blah blah
Thread.EndThreadAffinity();
So soll's nun hoffentlich in Ordnung sein. Bitte beachten, dass ProcessThread.Id keine Managed Thread ID ist. Auch ist der aktuelle ProcessThread in der ProcessThreadCollection i.d.R. nicht an Index 0.
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 22:03
von dot
Wie gesagt, die Geschichte mit der Thread Affinity ist ein ziemlich brutaler Workaround für uralte, verbuggte Hardware und ganz besonders auf modernen Systemen (XP aufwärts, Hardware jünger als 2005) ziemlich sicher hinfällig, da QPC dort in der Regel wohl den
HPET verwenden sollte, sofern das System nicht merkwürdig konfiguriert wurde (HPET im BIOS disabled, HAL per Bootoption zu anderem Verhalten gezwungen etc.). Und auch auf älteren Systemen sollte QPC noch eher den APIC verwenden als rdtsc (seit dem Tag, da ich QPC verwende, hab ich noch kein System gesehen, das nicht den APIC oder HPET verwendet). Diese ganzen Geschichten bezüglich der Probleme mit QPC gingen gerade um, als ich vor 10 Jahren angefangen hab und praktisch alle Quellen dazu stammen wohl auch aus dieser Zeit. Heutzutage gehört das wohl alles eher ins Land der Mythen...
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 22:07
von Niki
Ich tue das nicht hauptsächlich wegen verbuggter Hardware. Was mir wesentlich mehr zu denken gibt ist:
Microsoft DirectX SDK Documentation June 2012 hat geschrieben:
Compute all timing on a single thread. Computation of timing on multiple threads — for example, with each thread associated with a specific processor — greatly reduces performance of multi-core systems.
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 22:09
von dot
Da steht nur, dass du nicht in mehreren Threads gleichzeitig Timer auslesen sollst, was imo auch ziemlich einleuchtend ist. Da steht aber nichts von wegen dass du deinen Timer immer auf dem selben Core auslesen sollst... ;)
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 22:12
von Niki
Ich verstehe das anders. Für mich geht es darum auf welchem Prozessor der Thread läuft, und das ist nicht immer derselbe wenn man nichts dagegen tut.
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 22:21
von dot
Niki hat geschrieben:Ich verstehe das anders. Für mich geht es darum auf welchem Prozessor der Thread läuft, und das ist nicht immer derselbe wenn man nichts dagegen tut.
Und wo genau liest du das da oben heraus?
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 22:31
von Niki
dot hat geschrieben:Niki hat geschrieben:Ich verstehe das anders. Für mich geht es darum auf welchem Prozessor der Thread läuft, und das ist nicht immer derselbe wenn man nichts dagegen tut.
Und wo genau liest du das da oben heraus?
Einfach deshalb weil ich sonst keinen Grund sehe warum die explizit "with each thread associated with a specific processor" schreiben sollten. Deshalb verstehe ich es so, dass nicht der Thread-Wechsel das Performance-Problem ist, sondern der Prozessorwechsel.
Aber es kommt schon öfter mal vor, dass ich Dinge anders verstehe als andere :D Wäre also nicht verwunderlich wenn ich falsch liege. Allerdings reden wir bei C# von Managed Threads, und nicht von OS Threads. Ich wüsste nicht mal, ob ein Managed Thread den OS Thread wechseln kann.
Aber wer auch immer nun recht hat, ich bin jedenfalls happy, dass mein Zeug da läuft. Und wenn's doch mal Probleme macht, dann ab in die Tonne. Ist ja nicht so, dass ich den ganzen Code dafür umschreiben muss :)
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 22:35
von dot
Ich denk das steht da, da nur in diesem Fall die Timer auch tatsächlich parallel ausgelesen würden... ;)
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 22:57
von Niki
Also, die einzige Info die ich beim Googeln finde ist:
http://stackoverflow.com/questions/1723 ... -is-called
Der Poster ist aber auch nur ein Mensch. Also wer weiß schon was korrekt ist und was nicht.
Und nun wird's Zeit an meinem Editor weiter zu basteln. Ist ein bisserl mehr als nur Timing :D
Re: QueryPerformanceCounter in C#
Verfasst: 27.04.2013, 23:15
von dot
Da steht doch nur wieder der gleiche Nonsense von wegen rdtsc. Einfacher Reality-Check: Wenn dem so wäre, müsste die von QueryPerformanceFrequency() zurückgegebene Frequenz ja wohl der Frequenz der CPU entsprechen. Ich habe noch nie ein System gesehen, wo das der Fall wäre; Überraschenderweise kommt da normalerweise etwas in der Gegend von 3MHz (APIC) oder 25 MHz (HPET) zurück... ;)
EDIT: Grad mal mit dem Debugger in QueryPerformanceCounter() reingestepped und was seh ich? rdtsc :shock: Liegt aber vermutlich nur daran, dass CPUs mittlerweile invariant TSC implementieren und mein Sandy Bridge Core i7 da wohl keine Ausnahme ist...
Auf jeden Fall
MSDN hat geschrieben:On a multiprocessor computer, it should not matter which processor is called. However, you can get different results on different processors due to bugs in the basic input/output system (BIOS) or the hardware abstraction layer (HAL). To specify processor affinity for a thread, use the SetThreadAffinityMask function.
Re: QueryPerformanceCounter in C#
Verfasst: 28.04.2013, 01:05
von Schrompf
Ich kann hier nur kurz Erfahrungswerte einwerfen: RTDSC ist verlässlich auf allen Maschinen, die ich bisher gesehen habe. Mindestens 3 Millionen Takte pro Sekunde auf AMD-Hardware, Intel bietet üblicherweise Auflösungen auf Höhe der Nominal-Taktfrequenz - also 2 bis 4 Milliarden Takte pro Sekunde. Sieh zu, dass Du einen Datentyp benutzt, der mit solchen Auflösungen trotzdem noch Tage hinkommt, sonst ist all Dein Grübeln um veraltete BIOS-Bugs für den Arsch.
Re: QueryPerformanceCounter in C#
Verfasst: 28.04.2013, 01:37
von Niki
Ich benutze 64-Bit Integers, wie sie mir von der C# Stopwatch oder der WinAPI QueryPerformanceCounter gegeben werden. Für Berechnungen nehme ich 64-bit Integer Zeitdifferenzen, konvertiere die nach double und teile danach durch die Frequenz (auch nach double gecastet). Das muss gut genug sein.
Re: QueryPerformanceCounter in C#
Verfasst: 28.04.2013, 09:18
von Blue Cobold
Mit 15 korrekten Digits für Double sollte das eine Weile Laufzeit reichen, bis da ein Einschnitt in für Dich relevante Stellen erfolgt.
Angenommen Du brauchst es auf Mikrosekunden genau, dann ergäbe das etwa eine Laufzeit von mehreren Jahren, bis die Stellen zu ungebau werden.
Spannender numerisch gesehen ist eher die Frage, was Du mit den Zahlen dann anstellst. Differenzen ungünstig gewählter Zahlen führen ja bekanntlich bei Double schnell mal zu einer Zeitdifferenz von ... 0, bzw. ändert sich an der größeren Zahl bei der Differenz oder Summe nix. Da würde ich, wie Schrompf schon sagte, unbedingt ein Auge drauf haben, sonst ist die ganze Sache mit der hohen Zeit-Auflösung für die Katz.
Re: QueryPerformanceCounter in C#
Verfasst: 28.04.2013, 09:37
von Krishty
Er nimmt Integer-Differenzen, was für mich impliziert, dass er den letzten Zeitpunkt auch als Ganzzahl abspeichert und nur einzelne Frame-Zeiten als double rumreicht, nicht akkumulierte. Das dürfte völlig in Ordnung sein.
Re: QueryPerformanceCounter in C#
Verfasst: 28.04.2013, 10:43
von Niki
Ja, genau so wie Krishty sagt.
Die einzige Ausnahme die ich da mache ist, wenn ich Zeit für fixe Simulations-Zeitschritte akkumuliere.
Re: QueryPerformanceCounter in C#
Verfasst: 28.04.2013, 12:44
von Schrompf
Ja, das klingt gut. Solange alle Rechnungen mit int64 passieren und erst am Ende in double umgerechnet wird, sollte das alles ok sein. Ich hab mir damals einen int64 in eine kleine Klasse gekapselt und rechne seitdem recht entspannt damit. Nur ganz am Anfang hatte ich noch uint64 genommen, was sich aber bald als Fehler herausgestellt hat, weil man doch gelegentlich auch mal negative Zeitpunkte oder Zeitspannen bekommt.