Forum: PC-Programmierung Takt herausfinden - c++


von Wild (Gast)


Lesenswert?

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
von ich (Gast)


Lesenswert?

Vergiss es, ein moderner Prozessor (bzw. modernes Betriebssystem) wird 
da nie auch nur annähernd gleich lang warten.

von Wild (Gast)


Lesenswert?

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

von Klaus W. (mfgkw)


Lesenswert?

OS-Funktionen dafür nehmen (sleep(), Sleep(), seelcet()...)

von Klaus W. (mfgkw)


Lesenswert?

Wild schrieb:
> Andere programme arbeiten ja auch und fressen nur 20% CPU oder so.

Vielleicht weil sie in den anderen 80% auf IO warten :-)

von Klaus W. (mfgkw)


Lesenswert?

Klaus Wachtler schrieb:
> seelcet

das soll heißen: select

von Jasch (Gast)


Lesenswert?

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.

von Markus V. (Gast)


Lesenswert?

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

von Rolf Magnus (Gast)


Lesenswert?

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.

von wrg (Gast)


Lesenswert?

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)

von Jasch (Gast)


Lesenswert?

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.

von Joe R. (joer)


Lesenswert?

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
}

von Joe R. (joer)


Lesenswert?

äh, RDTSC nich RTDSC

von Vlad T. (vlad_tepesch)


Lesenswert?

und was passiert, wenn man mehrere Kerne hat und das OS zwischendurch 
den prozess von einem zum anderem schiebt?

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Nichts gutes, also besser die Betriebssystem-APIs verwenden, und nicht 
per Assembler auf dem Prozessor rummurksen.

von Michael R. (dj_motionx)


Lesenswert?

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

von Vlad T. (vlad_tepesch)


Lesenswert?

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

von Joe R. (joer)


Lesenswert?

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.

von Vlad T. (vlad_tepesch)


Lesenswert?

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.

von Joe R. (joer)


Lesenswert?

Das offenbart mir Dein Verständnis zur Problematik.
x86 machst Du nicht auf Systemebene, oder?

von Sven P. (Gast)


Lesenswert?

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.

von Joe R. (joer)


Lesenswert?

:-)

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?

von Sven P. (Gast)


Lesenswert?

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.

von Joe R. (joer)


Lesenswert?

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.

von Joe R. (joer)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Joe R. (joer)


Lesenswert?

Das ist schon geklärt, es geht nur noch um die Methode
zur Bestimmung der CPU Taktfrequenz.

Zum Warten nimmt man Sleep oder SleepEx

von Sven P. (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.
Lade...