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
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.
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.
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
longGetProcessorSpeed()
2
{
3
__int64MSRB,MSRE;
4
void*mrsb=&MSRB;// begin
5
void*mrse=&MSRE;// end
6
doublespeed1,speed2,dTime;
7
LONGLONGpcf,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
DWORDtcs=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
movebx,mrsb
30
movdwordptr[ebx],eax
31
movdwordptr[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
DWORDtce=GetTickCount();
41
QueryPerformanceCounter((LARGE_INTEGER*)&pce);
42
43
__asm
44
{
45
rdtsc
46
movebx,mrse
47
movdwordptr[ebx],eax
48
movdwordptr[ebx+4],edx
49
}
50
51
52
/* Verstrichene Zeit anhand des Zählers ermitteln, in Sekunden */
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.
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.