C++ Zeitmessung relativ?

T_55

Lieutenant
Registriert
Feb. 2013
Beiträge
638
Hallo,

neulich beim Rendern mit Cinema4D ist mir aufgefallen, dass die Geschwindigkeit nach einer gewissen Zeit absinkt. Ich vermute dies hängt damit zusammen, dass die CPU ab einer gewissen Temperatur drosselt. Die Kühlung im 15" Laptop des doch starken CPU hat scheinbar seine Grenzen daher hat man vermutlich solche Leißtungseinbußen sobald das System warm gelaufen ist.

Ich frage mich nun, bezogen auf Zeitmessungen von selbst geschriebenen Programmen, ob die Messungen nicht verfälscht werden wenn eine Messung, in der ersten Sekunden der CPU Balstung, mit einer Messung nach 10 Minuten CPU Belastung, verglichen wird.

Zeitmessungen mache ich mit std::chrono::high_esolution_clock::now()
Wird damit die "echte Zeit" gemessen wäre es eigentlich nicht gut aus oben genannten Gründen.
Gibt es eine Zeitmessung die "neutral" gegenüber CPU-Drosselung oder Belastung andere Prozesse etc. ist?

Gruß
 
Lustiger Gedanke.. Also wirklich. Deine Systemuhr läuft wohl auch langsamer, wenn die CPU belastet wird. :freak:
 
Deswegen sollte man ja bei Messungen Boost modi deaktivieren und Throttling Vermeiden. Ist beides nicht vermeidbar muss man eben sehr lange und unter einer gleichbleibenden Umgebungstemperatur messen. :)
 
Also meiner Meinung nach sollte es hier eine Abweichung geben wie groß diese sind kann ich dir aber nicht sagen. Bei Anwendungen die Kritisch auf Zeit reagieren wie zum beispiel in der Regelungstechnik gibt es extra Echtzeitsysteme. Wie ein solches Funktioniert kann ich dir allerdings nicht sagen. Nach der Aussage unseres Professors für Regelungstechnik verfälscht zum beispiel schon das bewegen der Maus ein Matlab Ergebnis durch die dafür benötigte CPU Leistung.
 
Zuletzt bearbeitet:
Dieses Problem gab es bei 286 und 386 (mit Turbo waren Spiele unspielbar). Seit es eine RTC (real time clock) in der CPU/auf dem Mainboard gibt, hat sich das Problem erledigt. Wie Diese in C++ integriert oder erreichbar ist, weiß ich allerdings nicht.
 
Nach der Aussage unseres Professors für Regelungstechnik verfälscht zum beispiel schon das bewegen der Maus ein Matlab Ergebnis durch die dafür benötigte CPU Leistung.

Aber nur im absoluten Promille-Bereich oder? Selbst die Pentium 1 CPUs aus Mitte der 90er sind von Mausbewegungen absolut unbeeindruckt.
 
@Seby007 kann schon sein. Ich kann dir darauf aber keine richtige Antwort geben. Bei Praktischen anwendungen von Reglern ist aber ein Echtzeitsystem pflicht. Da ich aber nicht direkt System und Regelungstechnik Studiere sonder Maschinenbau und das bei uns nur nen "Nebenfach" war kann ich dir da drüber nicht wirklich viele Infos geben :D. Btw: es war wohl das schlimmste Fach in meinen ganzen MB Bachelor.
 
@T_55, für den Dauerbetrieb unter Last wird von den CPU Herstellern nur der Basistakt garantiert, sogar in Abhängigkeit von Instruktionen, wie AVX. Bei GPU ist es ähnlich. Nvidia Quadro GPU werden aus diesem Grund mit einem niedrigeren Takt ausgeliefert, als GeForce GPU. Für eine Quadro P2000 sind es bspw. ~230 Mhz (~15%) weniger, im Vergleich zum Boost-Modus einer GeForce GTX 1060. Beim Kauf sollte man generell auf einen hohen Basistakt achten!
 
Edit:
Frage falsch verstanden. Kurz gesagt, wenn du das Ergebnis von zwei

std::chrono::high_esolution_clock::now()

Aufrufen vergleichst erhälst du immer eine Echtzeit, also unabhängig von der gerade anliegenden Prozessorfrequenz (wenn sich deine CPU im Idle runter taktet geht die Uhr z.B. auch nicht langsamer). Afaik gibt es Performancecounter / Systemcalls, die sich so verhalten, aber nicht die c++ chrono clocks.
 
Zuletzt bearbeitet:
Danke schonmal für den Input. Das heißt also ich messe die Echtzeit was ja bedeuten würde das genannte Effekte die Zeitmessung verzerren könnten.

Es geht ja bei Messungen darum herauszufinden welcher Code effizient ist. Inzwischen habe ich herausgefunden das scheinbar die "CPU-Zeit" das ist was ich suche und nochmal was ganz anderes ist.

clock() von time.h scheint entsprechend zu passen
oder
Boost.Timer, das scheint mir am ausgereiftesten da man bei der CPU Zeit noch unterscheiden kann zwischen user space und kernel space bzw als Tuple dann wall, user und system.

bei std::chrono konnte ich keine so wirklichen Erklärungen finden was genau eigentlich gemessen wird. Denke ich tendiere daher zu Boost.Timer für Performance-Messungen. Prozessordrosselung und etc müssten somit eher kein EInfluss mehr haben und die Messung werden neutraler und seriöser.
 
Zuletzt bearbeitet:
Wie kommst du auf die idee, Boost.Timer würde den Einfluss von Turbo/Drosselung eliminieren? Der gibt dir genauso eine Echtzeit aus (und z.B. keinen cpu cycle count)

Was clock() angeht ist die Funktion unter windows leider nicht standardkonform implementiert und dort ziemlich nutzlos.

Was man bei der ganzen Sache nicht vergessen darf:
Du kannst die Effizienz einer Lösung ohnehin nicht komplett unabhängig von der Prozessorfrequenz angeben. Du kannst zwar Prozessorzyklen zählen, aber viele Operationen (insbesondere Speicherzugriffe) skalieren nicht linear mit der Prozessorfrequenz. Soll heißen bei unterschiedlichen Frequenzen könnten unterschiedliche Lösungen am effizientesten sein - allerdings ist das relativ unwahrscheinlich.

Meine persönliche Meinung: Wenn man den Performanceunterschied zwischen zwei Lösungen nicht zuverlässig und reproduzierbar auf einem Desktopsystem feststellen kannst, das ansonsten gerade im Idle ist, dann ist er nicht groß genug, um die Entscheidungen danach auszurichten (gilt natürlich nicht für code der millionenfach im datencenter ausgeführt wird und wo selbst ein paar prozent schon millonen von euro einsparen können).

Natürlich will man nicht, dass der innitiale Turbo das Ergebnis verfälscht, aber normalerweise sollte das nur bei LowPower/Notebookprozessoren ein Problem sein und unter anderem deshalb lässt man enrsthafte Benchmarks ja auch nicht nur ein paar Sekunden laufen.
 
T_55 schrieb:
Danke schonmal für den Input. Das heißt also ich messe die Echtzeit was ja bedeuten würde das genannte Effekte die Zeitmessung verzerren könnten.

Falsch. Zeitwerte stammen bis auf Ausnahmen immer von der Systemuhr und die geht unabhängig vom Zustand des restlichen Systems immer gleich.

Es geht ja bei Messungen darum herauszufinden welcher Code effizient ist. Inzwischen habe ich herausgefunden das scheinbar die "CPU-Zeit" das ist was ich suche und nochmal was ganz anderes ist.

Jain. Um die Effizienz von Code zu bewerten ist die CPU-Zeit nicht unbedingt hilfreich. Die Ausführungszeit des Programms kann sehr hoch sein, während die CPU-Zeit vergleichsweise gering ist, wenn der Code unnötig viele I/O-Operationen erzwingt (frisst Laufzeit bei nahezu null CPU-Zeit). Das würde man typischerweise als ineffizient bezeichnen. Genauso kommt ein gut auf verschiedene Threads skalierendes Programm auf eine hohe CPU-Zeit bei geringer Laufzeit und wäre damit typischerweise sehr effizient.
Die CPU-Zeit misst ansonsten wirklich nur die Zeit die für die Ausführung benötigt wirdund gibt keinen Aufschluss ob die CPU nun während der Ausführung irgendwelche Turbomodi nutzte oder stark gedrosselt war.
Ein anderer Ansatz um Effizienz von Algorithmen zu bewerten wäre ansonsten die "Bit O notation" (als guter Schätzer)



clock() von time.h scheint entsprechend zu passen
oder
Boost.Timer, das scheint mir am ausgereiftesten da man bei der CPU Zeit noch unterscheiden kann zwischen user space und kernel space bzw als Tuple dann wall, user und system.

Ich glaube ja, dass du selbst noch nicht weiß, welche Aussage du treffen willst und entsprechend gerade überhaupt nicht weißt, welche Messwerte du brauchst.


bei std::chrono konnte ich keine so wirklichen Erklärungen finden was genau eigentlich gemessen wird. Denke ich tendiere daher zu Boost.Timer für Performance-https://www.computerbase.de/forum/Messungen. Prozessordrosselung und etc müssten somit eher kein EInfluss mehr haben und die Messung werden neutraler und seriöser.

std:chrono ist doch aber wirklich recht ausführlich dokumentiert?!
Was du mit Boost.time willst ist mir auch unklar, oder besser wie oben bereits erklärt ist mir und wohl auch dir unklar welche Aussagen du treffen willst und damit auch die notwendige Methodik die zu wählen wäre um eine Aussage treffen zu können.

Typischerweise wird jedoch um Performance zu bewerten schlicht und ergreifend die Laufzeit gemessen und bei gleichen Umgebungsbedingungen verschiedene Laufzeiten miteinander verglichen. Als Anwender ist einem ja wichtig wie lang man zu warten hat und nicht wie viel CPU_Zeit aufgewendet wurde.
 
Zuletzt bearbeitet:
Hatte mich da auf diese Aussagen bezogen:

http://www.boost.org/doc/libs/1_53_0/libs/timer/doc/cpu_timers.html
"To study the efficiency of code, total CPU time (user + system) is often a much better measure."

https://dieboostcppbibliotheken.de/boost.timer
"Wann immer Sie die Performance Ihres Codes messen möchten, sollten Sie sich Boost.Timer zuwenden und nicht Boost.Chrono."
"Die CPU-Zeit gibt den Anteil der tatsächlichen Zeit an, in der Code des Programms ausgeführt wurde."

http://www.willemer.de/informatik/cpp/timelib.htm
"Tickzählung per clock() Mit der Funktion clock() wird die verbrauchte CPU-Zeit ermittelt. Das heißt, dass Zeiten anderer parallel laufender Prozesse nicht in die Zeitmessung mit einfließen. Dazu werden die verbrauchten CPU-Ticks seit dem Start des Programms ermittelt. Um auf Sekunden zu kommen, muss der Wert durch die Konstante CLOCKS_PER_SEC geteilt werden. Diese Funktion eignet sich vor allem für Performance-Messungen. "

Der Unterschied zwischen wall time und cpu time ist doch wohl der Knackpunkt und alle sagen für Codeeffizienz prüfen ist die CPU-Time besser.
Nochmal zur Übersicht:
wall-time / real-time : Echtzeit
user-time: Prozesszeit in User-Mode (Berechnungen)
sys-time: Prozesszeit in Kernel-Mode (Festplattenzugriffe,Synchronisationen o.ä.)

und std::chrono ist scheinbar nur wall time
std::chrono::high_resolution_clock http://en.cppreference.com/w/cpp/chrono/high_resolution_clock
"Class std::chrono::system_clock represents the system-wide real time wall clock. "
 
Sofern ich mich errinnere ist QueryPerformanceCounter() und QueryPerformanceFrequency() unabhängig vom Throttling oder Boosts. Man holt sich daher am Anfang des Programmstarts die Frequenz und nutzt diese dann beim Zeitmessen mit QueryPerformanceCounter() um in Sekunden umzurechnen.

Das ist mit ziemlicher Sicherheit genau das was in std::clock genau für Win32 implementiert ist - sofern man high_resolution_clock haben will.
 
Wieso so umständlich, einfach die RTC abfragen. Die ist unabängig von der Taktfrequenz und schon seit vielen Generationen in allen x86 CPUs vorhanden. Das letzte mal, dass ich von Timing-Problemen in Verbindung mit Taktänderungen gehört habe war bei UT99. Die hatte ursprünglich wohl beim Programmstart die Taktfrequenz ermittelt und dann ihre Zeitberechnung darauf aufbauend selbst gemacht. Als dann AMD Cool&Quiet für Desktop-CPUs eingeführt hat war plötzlich alles viel zu schnell, weil die Zeitberechnung auf der Idle-Taktfrequenz basierte.
 
Herje

Zu Anfang: Die Zeitmessung allein bringt dir weder eine Aussage über die Performance noch Effizienz. Egal mit welcher Messmethode. Die gemessenen Zeitwerte sind genau nur das, gemessene Zeitwerte, die für die Ausführung benötigt werden. Diese Werte für sich lassen keine Aussagen wie effizient Programmcode ist oder wie performant. Die Zeitwerte skalieren ja neben dem Verhalten des Codes ebenso mit der Leistungsfähigkeit der Hardware und bedingt mit dem restlichen Zustand des Systems (allen voran Betriebssystem und aller gleichzeitig laufender Programme sowie Interrupts). Das Auslesen de CPU Zeit und das Aufsplitten ob diese im Kernel- oder Userspace lässt minimal andere Aussagen zu. Da steht aber nach wie vor die Frage im Raum, welche Aussagen du mit deinen Tests genau treffen willst.

Auf keinem Fall ist jedoch die CPU-Zeit in irgend einer Form auf die Leistungsfähigkeit der CPU normiert. Daher, die CPU Zeit zählt eine Sekunde hoch wenn eine Sekunde vorbei ist. Egal ob die Cpu gerade im Turbo ist oder drosselt bzw. gerade AVX mit 512bit Breite durchnudelt oder einzelne 8bit Integer addiert. Ganz abgesehen davon, dass der ganze Spaß bei CPUs mit SMT und ähnlichen Späßen ganz fix noch eine Ecke komplexer wird. In der Regel will man bzw. braucht man diese komplexe Betrachtung auch einfach nicht und die Laufzeit eines wohl definierten Problems zu messen und Werte von verschiedenen System zu vergleichen ist ausreichend (unter der Bedingung, dass das lösen des Problems die einzige, signifikante Last aus den zu vergleichenden Systemen stellt).
 
Die Aussage die ich mit dem Test treffen will ist, welcher Code ist schneller. Im Prinzip hab ich einen methodischen Fehler gemacht, ich werd die Vorgehensweise jetzt dahingehend abwandeln, dass ich die Dinge nicht mehr getrennt durchiteriere, messe und dann zum Nächsten springe, sondern die Ausführung der zu vergleichenden Codes gemischt iteriere und die Werte jeweils aufaddiere.
So liegt die Ausführung aller Codevarianten zeitlich zwangsläfig so eng zusammen, dass CPU-Throttling keine Verzerrung verursacht. So wird jede Variante zu jeder Bedingung (CPU-Temp, Zeitpunkt, etc) durchgenudelt.
Statt beispielsweise zwei zu vergleichende Varianten jeweils getrennt hintereinander 30 Sekunden zu iterieren, werden beide Varianten gemeinsam 60 Sekunden gemischt iteriert. Wenn dann am Ende die CPU langsamer wird betrifft es beide gleichermaßen und es gibt keine Verzerrung in der Messung zueinander.
 
Herje die Zweite

Es klingt so als hättest du sehr kleine Tests. Entspricht es dem realem Problem, dass der Code nur so kurz läuft? Wenn nicht solltest du deine Testfälle auf ein reales Maß ausblasen. Es bringt dir ja wenig, wenn deine Testfälle so klein sind, dass sie fast komplett in den CPU-Cache passen während die realen Fälle eher vom Ram bzw. dem Festspeicher bedient werden.

Wenn du Probleme mit dem Throttling hast, mach halt die Benches, lass dazwischen 1-2Minuten Pause und teste danach die zweite Variante. Im Zweifelsfall lässt du die Kiste vorher mit einem Durchlauf warm werden.
 
Zuletzt habe ich Ringspeicher verglichen bei dem es nur um auslesen oder push back geht. Diese Zugriffe führe ich im Test natürlich entsprechend häufig und mehrfach durch, sonst kommt gar keine messbare Dauer raus. Die gängigen Datenstrukturen zu hinterfragen lohnt sich aber schon extrem. Ein Ringspeicher, je nachdem per deque, boost::circular_buffer, modulozeugs, pointerzeugs oder etliche weitere Varianten aus dem www haben drastische Unterschiede. Am Ende war mein selbst erstelltes Struct mit allen nötigen Methoden wesentlich schneller als jede andere Variante (sogar doppelt so schnell wie boost::circular_buffer). Aus dem Grund baue zur Zeit immer öfter meine Datenstrukturen selber mit den entsprechend benötigten Zugriffmethoden, daher hab ich gerade soviel Spaß an dem Thema Performancemessung :)
 
Zurück
Oben