Hey Leute! Ich müsste am PC in C++ (Code::Blocks als IDE) die Taktrate vom Prozessor herausfinden um eine Warteschleife zu machen (mit nop's). Ich will den Prozessor nicht voll auslasten mit diftime() oder so. Hat jemand irgendeine Idee? Wild
:
Verschoben durch Moderator
Vergiss es, ein moderner Prozessor (bzw. modernes Betriebssystem) wird da nie auch nur annähernd gleich lang warten.
Aber wie schaff ich es zu warten ohne den Prozessor voll auszulasten ? Andere programme arbeiten ja auch und fressen nur 20% CPU oder so. Wild
Wild schrieb: > Andere programme arbeiten ja auch und fressen nur 20% CPU oder so. Vielleicht weil sie in den anderen 80% auf IO warten :-)
Wild schrieb: > Aber wie schaff ich es zu warten ohne den Prozessor voll auszulasten ? Na jedenfalls nicht mit NOPs in einer Schleife. Das ist klassisches Busy-Waiting und lastet die CPU zu genau 100% aus.
Erschwerend kommt hinzu, dass bei modernen CPUs der Takt keine konstante Größe ist. Ein möglicher Ansatz um zu warten ist das Aufziehen eines Timers und auf das Timerevent beim Ablauf desselben zu warten. Gruß Markus
Markus V. schrieb: > Erschwerend kommt hinzu, dass bei modernen CPUs der Takt keine konstante > Größe ist. Und daß die Laufzeit einer Instruktion bei weitem nicht mehr nur davon abhängt.
Rolf Magnus schrieb: > Markus V. schrieb: >> Erschwerend kommt hinzu, dass bei modernen CPUs der Takt keine konstante >> Größe ist. > > Und daß die Laufzeit einer Instruktion bei weitem nicht mehr nur davon > abhängt. es gibt aber die moeglichkeit, das zaehlregister der cpu auszulesen, das die cycles since reset mitzaehlt. am besten nimmst du aber den befehl usleep(int zu_schlafende_mikrosekunden)
wrg schrieb: > Rolf Magnus schrieb: >> Markus V. schrieb: >>> Erschwerend kommt hinzu, dass bei modernen CPUs der Takt keine konstante >>> Größe ist. >> >> Und daß die Laufzeit einer Instruktion bei weitem nicht mehr nur davon >> abhängt. > > es gibt aber die moeglichkeit, das zaehlregister der cpu auszulesen, das > die cycles since reset mitzaehlt. Da gibt es aber wohl CPUs die Taktänderungen wegen Powermanagement da berücksichtigen oder auch nicht - der Weg führt also auch in die Hölle. Vom OS bereitgestellte Funktionen sind schon der einzige Weg, es sei denn es muss hochgenau sein - dann kann man es auf üblichen Rechnern mit üblichen Betriebssystemen einfach nur vergessen. Sind nicht dafür vorgesehen, man braucht dafür dann ein RTOS.
x86 CPU -> RTDSC http://en.wikipedia.org/wiki/Time_Stamp_Counter
1 | long GetProcessorSpeed() |
2 | {
|
3 | __int64 MSRB, MSRE; |
4 | void *mrsb = &MSRB; // begin |
5 | void *mrse = &MSRE; // end |
6 | double speed1, speed2, dTime; |
7 | long hSpeed1, lSpeed1, hSpeed2, lSpeed2; |
8 | LONGLONG pcf, pcs, pce; |
9 | |
10 | QueryPerformanceFrequency((LARGE_INTEGER *) &pcf); |
11 | Sleep(1); |
12 | DWORD tcs = GetTickCount(); |
13 | QueryPerformanceCounter((LARGE_INTEGER *) &pcs); |
14 | |
15 | __asm
|
16 | {
|
17 | rdtsc
|
18 | mov ebx, mrsb |
19 | mov dword ptr [ebx], eax |
20 | mov dword ptr [ebx+4], edx |
21 | }
|
22 | |
23 | Sleep(1000); |
24 | |
25 | DWORD tce = GetTickCount(); |
26 | QueryPerformanceCounter((LARGE_INTEGER *) &pce); |
27 | |
28 | __asm
|
29 | {
|
30 | rdtsc
|
31 | mov ebx, mrse |
32 | mov dword ptr [ebx], eax |
33 | mov dword ptr [ebx+4], edx |
34 | }
|
35 | |
36 | dTime = ((double)((pce - pcs)) / ((double)pcf)); |
37 | speed1 = ((double)(MSRE - MSRB) / (dTime * 1000000)); |
38 | speed2 = ((double)(MSRE - MSRB) / ((double)(tce - tcs) * 1000)); |
39 | |
40 | hSpeed1 = (long) speed1; |
41 | lSpeed1 = (long) ((speed1 - hSpeed1) * 100.0); |
42 | |
43 | hSpeed2 = (long) speed2; |
44 | lSpeed2 = (long) ((speed2 - hSpeed2) * 100.0); |
45 | |
46 | return ((long)(speed1 * 1000) > (long)(speed2 * 1000) ? (long)(speed1 * 1000) : (long)(speed2 * 1000)); // in kHz |
47 | }
|
und was passiert, wenn man mehrere Kerne hat und das OS zwischendurch den prozess von einem zum anderem schiebt?
Nichts gutes, also besser die Betriebssystem-APIs verwenden, und nicht per Assembler auf dem Prozessor rummurksen.
Hallo ! Kann euch empfehlen "select" zu verwenden. http://www.zotteljedi.de/socket-tipps/
1 | struct timeval tv |
2 | |
3 | // Wait for one second
|
4 | tv.tv_sec = 1; |
5 | tv.tv_usec = 0; |
6 | select(0,NULL,NULL,NULL,&tv); |
Wartet für eine Sekunde Mfg Michi
Andreas Schwarz schrieb: > Nichts gutes, also besser die Betriebssystem-APIs verwenden, und nicht > per Assembler auf dem Prozessor rummurksen. genau das wollte ich damit ausdrücken
Vlad Tepesch schrieb: > und was passiert, wenn man mehrere Kerne hat und das OS zwischendurch > den prozess von einem zum anderem schiebt? SetThreadAffinityMask Aber alles kann ich Euch nicht vorbeten... Es gibt immer eine Lösung. @Vlad, wie dachtest Du macht es das OS? Welche API, wenn das Produkt auch auf W2k laufen muss? Der Kunde ist König, nicht der Developer. Apropo, die Routine ist u. a. dazu da den Jitter zu messen den der Board-Designer per Default im BIOS aktiviert hat, damit die faule Sau durch die EMV Prüfung kommt. :-) Als Testprogramm für Audio-Workstations.
Joe Redfish schrieb: > SetThreadAffinityMask > > Aber alles kann ich Euch nicht vorbeten... > Es gibt immer eine Lösung. mir ist schon klar, dass man einen Prozess an bestimmte logische Kerne binden kann, aber das kann ja nicht sinn und zweck des Programms sein, selbst zu entscheiden, wo es läuft. Wenn das alle machen würden, gäbe es einen maximal ausgelasteten Kern 0 und kein Programm würde auf den anderen 1,3,7 Kernen laufen. und wenn der TO schon schreibt: Wild schrieb: > Ich müsste [...] die Taktrate vom > Prozessor herausfinden um eine Warteschleife zu machen (mit nop's). [...] weiß man, dass das der komplett falsche Ansatz ist. Joe Redfish schrieb: > @Vlad, wie dachtest Du macht es das OS? > Welche API, wenn das Produkt auch auf W2k laufen muss? > Der Kunde ist König, nicht der Developer. ist irgendwas von Joe Redfish schrieb: > QueryPerformanceFrequency((LARGE_INTEGER *) &pcf); > Sleep(1); > DWORD tcs = GetTickCount(); > QueryPerformanceCounter((LARGE_INTEGER *) &pcs); kein API-Call? zumal du doch selbst schon eine API-funktion callst, um eine bestimmte Zeit zu warten. warum sollte man mit so einer bescheuerten Methode die Frequenz messen um dann, Frequenz (die sich ständig ändert) einmal ermittelt, mit NOPs zu warten? Joe Redfish schrieb: > Apropo, die Routine ist u. a. dazu da den Jitter zu > messen glaub ich nicht. damit misst du alles mögliche, aber sicher keinen Jitter.
Das offenbart mir Dein Verständnis zur Problematik. x86 machst Du nicht auf Systemebene, oder?
Joe Redfish schrieb: > Das offenbart mir Dein Verständnis zur Problematik. > x86 machst Du nicht auf Systemebene, oder? Und dir ist bei deinem Zufallsgenerator auch aufgefallen, dass es Out-of-order-execution gibt? Die Aufrufe der API-Funktionen können unterschiedlich lange dauern, je nachdem, was zum Beispiel die Speicherverwaltung gerade macht, wenn du Stapelspeicher für den Aufruf brauchst. Sleep ist für Zeitmessungen total daneben, guck mal, was das an Auflösung bringt.
:-) schau mal richtig bitte. Da gibt es 3 Zeitmessungen die verglichen werden. Sleep ist keine davon. Ist das so, das alle nur oberflächlich schauen, ein Urteil abgeben und denken sie wissen es besser? In China und Indien haben sie mehr Zeit zum überlegen. Krankt der deutsche Standort am Ende daran? Sleep setzt den Scheduler zurück, nicht gewusst?
Joe Redfish schrieb: > schau mal richtig bitte. > Da gibt es 3 Zeitmessungen die verglichen werden. > Sleep ist keine davon. Ich seh da nur einen mäßigen Zufallsgenerator und etwas wegoptimierte Rechnerei.
1 | long GetProcessorSpeed() |
2 | {
|
3 | __int64 MSRB, MSRE; |
4 | void *mrsb = &MSRB; // begin |
5 | void *mrse = &MSRE; // end |
6 | double speed1, speed2, dTime; |
7 | LONGLONG pcf, pcs, pce; |
8 | |
9 | /* Zählfrequenz ermitteln */
|
10 | QueryPerformanceFrequency((LARGE_INTEGER *) &pcf); |
11 | |
12 | /* Mindestens eine Zeitscheibe warten */
|
13 | Sleep(1); |
14 | |
15 | /* Die Systemzeit mit einer Auflösung von etwa 10ms ermitteln */
|
16 | DWORD tcs = GetTickCount(); |
17 | |
18 | /* Zähler mit unnötigem Typecast abfragen */
|
19 | QueryPerformanceCounter((LARGE_INTEGER *) &pcs); |
20 | |
21 | /* Willkürlich den Zeitstempel auslesen, ohne irgendwie die Hinweise im Prozessormanual
|
22 | gelesen zu haben. Da steht zum Beispiel, dass der Zeitstempel nicht auf allen Prozessoren
|
23 | (Mehrkernsystem) synchronisiert sein muss (AMD) oder unabhängig von der Taktfrequenz ist
|
24 | (Intel Speedstep bei Pentium M). Oder auch Out-of-order-execution, wogegen etwa 'rdtscp'
|
25 | statt 'rdtsc' helfen würde. */
|
26 | __asm
|
27 | {
|
28 | rdtsc
|
29 | mov ebx, mrsb |
30 | mov dword ptr [ebx], eax |
31 | mov dword ptr [ebx+4], edx |
32 | }
|
33 | |
34 | /* Etwa eine Sekunde (typischerweise plus/minus 10ms) warten */
|
35 | Sleep(1000); |
36 | |
37 | /* Nochmal ungenaue Systemzeit und Zähler abfragen. Niemand weiß, wie lange die beiden
|
38 | Funktionsaufrufe samit Rücksprung und Zuweisung dauern.
|
39 | Oder ob sie wenigstens genauso lange dauern, wie oben. */
|
40 | DWORD tce = GetTickCount(); |
41 | QueryPerformanceCounter((LARGE_INTEGER *) &pce); |
42 | |
43 | __asm
|
44 | {
|
45 | rdtsc
|
46 | mov ebx, mrse |
47 | mov dword ptr [ebx], eax |
48 | mov dword ptr [ebx+4], edx |
49 | }
|
50 | |
51 | |
52 | /* Verstrichene Zeit anhand des Zählers ermitteln, in Sekunden */
|
53 | dTime = ((double)((pce - pcs)) / ((double)pcf)); |
54 | |
55 | /* Damit 'Takte' pro Zeit berechnen */
|
56 | speed1 = ((double)(MSRE - MSRB) / (dTime * 1000000)); |
57 | |
58 | /* Gleiches anhand der Systemzeit berechnen */
|
59 | speed2 = ((double)(MSRE - MSRB) / ((double)(tce - tcs) * 1000)); |
60 | |
61 | /* Den größeren Wert liefern */
|
62 | return ((long)(speed1 * 1000) > (long)(speed2 * 1000) ? (long)(speed1 * 1000) : (long)(speed2 * 1000)); // in kHz |
63 | }
|
Sorry, was wolltest du noch gleich schätzen...? Das, was du da gerade treibst, ist reines Pokerspiel. Du hast ja nichtmal dafür gesorgt, dass der Prozessor dir deinen Programmtext nicht umsortiert. Du verlässt dich blind auf irgendein unbekanntes Verhalten des Compilers.
Das hast Du aber schön kommentiert. Entgegen Deinen Vermutungen funktioniert es aber auf allen alten wie aktuellen Prozessoren. Bedingung: - Thread auf einen Prozessor festnageln (NICHT PROZESS) - zweiten Thread mit Prozessorauslastung beauftragen Wozu das Sleep wirklich nützlich ist fehlt noch in Deinem Text: Es setzt den Scheduler zurück. SwitchToThread(); geht aber auch. Wenn es zurückkehrt, wird es für die paar Zyklen keinen Threadwechsel geben. Je nach Quantumeinstellung mindestens 3ms. Das genügt für die 2 Funktionen. Und ja, ich habe es gemessen wie lange die brauchen: IMMER GLEICH VIEL ZEIT - daher ist es egal. Die zweite Variante ist für Systeme ohne Highperformancecounter. Welche Lösung hättest Du denn vorzuschlagen? > Ich seh da nur einen mäßigen Zufallsgenerator und etwas wegoptimierte > Rechnerei. Dieser Satz ist reines Imponiergehabe und entbehrt jeder Grundlage. Bleib bitte sachlich. Falls Du was davon verstehst, kompiliere die Funktion und messe selbst. Ich muss hier Niemandem etwas beweisen.
Ich habe das out of order Problem analysiert und es ist zutreffend das einige Takte falsch gezählt werden kann wenn nicht genau definiert ist welche Befehle waren oder kommen, da aber nach einem Flush beim Threadwechsel immer die gleiche Befehlsfolge kommt, hebt sich eine eventuelle out of order Konstellation gegenseitig auf. Der zu erwartende Fehler ist bei der Messzeit von einer Sekunde tolerierbar. Der Prozessortakt in Kiloherz als LONG ist ausreichend. Siehe return. Da können gern ein paar Takte verschenkt werden. Trotzdem Danke für den Rat, RDTSCP läuft aber nicht auf allen Zielplattformen. Man könnte CPUID einbauen zur Serialisierung. Seite 10 http://download.intel.com/embedded/software/IA/324264.pdf
Wo braucht man Verrenkungen mit aktiven Warteschleifen bei PCs eigentlich? Ausserhalb von Device Drivern ist es grober Unfug. Und auch nur halbwegs aktuelle Devices bei heutigen PCs, die aktiv verbrachte Wartezeiten unterhalb der Grenze zum Scheduling verlangen, sollte man den Herstellern eigentlich um die Ohren hauen.
Das ist schon geklärt, es geht nur noch um die Methode zur Bestimmung der CPU Taktfrequenz. Zum Warten nimmt man Sleep oder SleepEx
Ich würde eine geeignete API dafür benutzen/suchen. Du betreibst weiterhin pure Spekulation darauf, was Compiler und Betriebssystem machen. Die Zeitscheiben und der Scheduler für den Userspace sind Näherungswerte, spätestens auf Treiber/Kernelebene interessieren die niemanden mehr wirklich. Wenn deine MMU dann auf einmal anfängt, Speicher auszulagern, hast du wieder ein Problem.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.