clock() verhält sich seltsam



  • Hallo,
    ich habe vorhin erstmals probiert, die Laufzeit von einigen meiner Programme zu prüfen. Allerdings habe ich ein ziemliches Problem: Weise ich einer Variable "clock()" zu, so wird die Variable einfach mit Null belegt.
    Wenn ich diesen Beispiel-Quelltext von cplusplus.com ausprobiere, scheint alles gut zu laufen:

    #include <stdio.h>
    #include <time.h>
    
    void wait ( int seconds )
    {
      clock_t endwait;
      endwait = clock () + seconds * CLOCKS_PER_SEC ;
      while (clock() < endwait) {}
    }
    
    int main ()
    {
      int n;
      printf ("Starting countdown...\n");
      for (n=10; n>0; n--)
      {
        printf ("%d\n",n);
        wait (1);
      }
      printf ("FIRE!!!\n");
      return 0;
    }
    

    Wenn ich jedoch so etwas probiere:

    #include <stdio.h>
    #include <time.h>
    
    int main()
    { clock_t Anfang , Ende;
      Anfang = clock();
      Anweisungen;
      Ende = clock();
    
      printf("%lu %lu\n" , Anfang , Ende);
      return(0);
    }
    

    So sind bei mir stets beide Variablen Null. Ich habe auch schon das Umrechnen in doubles sowie in Einheiten von Sekunden probiert, allerdings erhalte ich stets Nullen. Kann das am Compiler liegen? (ich habe Ubuntu 9.04 und eclipse)



  • Hi!
    Das liegt daran, das deine Zeit für die Anweisungen zu kurz ist.



  • Zeit zu kurz? Ich habe vorhin ein Programm dieser Art vorgeführt bekommen und die Zeiten lagen teilweise im Bereich 10^-4 Sekunden. Viel schneller kommt mir mein Programm gar nicht vor ;).

    Außerdem habe ich auch mal bei einer angeblich recht zeitaufwändigen Aktion gemessen (zwei for-Schleifen, die insgesamt ca. 1000 reallocs durchführen), da kam auch nur Null raus.

    Zudem fiel mir ein, dass ich im BIOS mal so eine Option gesehen habe, die was mit Zeit zu tun hatte. Ich habe eben mal schnell nachgeschaut und zwei Einstellungen gefunden, die vielleicht was damit zu tun haben könnten (?):
    High Precision Event Timer (HPET) --> enabled
    HPET-Genauigkeite --> 32 oder 64 Bit (habe schon 32- und 64-Bit-Variante erfolglos getestet)

    EDIT: Ich habe mir gerade den Wiki-Artikel durchgelesen (http://en.wikipedia.org/wiki/High_Precision_Event_Timer), vielleicht hat es wirklich was mit meinem Motherboard zu tun. Aber da ich keine Ahnung habe, wie C sowas handhabt, muss ich wohl doch auf Tipps von euch warten :).

    EDIT2: Im übirgen hatte ich von clock erwartet, dass es CPU-Zyklen oder sowas zählt, Nullen dürfte ich in dem Fall aber praktisch nie erhalten. 😕



  • Stiefel2000 schrieb:

    Zeit zu kurz? Ich habe vorhin ein Programm dieser Art vorgeführt bekommen und die Zeiten lagen teilweise im Bereich 10^-4 Sekunden. Viel schneller kommt mir mein Programm gar nicht vor ;).

    clock hat normalerweise millisekunden als einheit und schaltet alle 1/18.2 sekunden um einen betrag von ungefähr 1000/18,2=55 millisekunden weiter.
    das kommt noch aus DOS, wo der timer einfach so eingestellt war.
    10^-4 sekunden kannste damit gar nicht greifen.

    Außerdem habe ich auch mal bei einer angeblich recht zeitaufwändigen Aktion gemessen (zwei for-Schleifen, die insgesamt ca. 1000 reallocs durchführen), da kam auch nur Null raus.

    noch unter 55ms?

    Zudem fiel mir ein, dass ich im BIOS mal so eine Option gesehen habe, die was mit Zeit zu tun hatte. Ich habe eben mal schnell nachgeschaut und zwei Einstellungen gefunden, die vielleicht was damit zu tun haben könnten (?):
    High Precision Event Timer (HPET) --> enabled
    HPET-Genauigkeite --> 32 oder 64 Bit (habe schon 32- und 64-Bit-Variante erfolglos geteste)

    die hilft allem möglichen im betriebssystem. da kann es hpet nehmen statt rdtsc oder standardzugriffe auf die teure realtimeclock sparen oder so. das kannst du als nichtbetriebssystem aber nicht gescheit erfahren. alles, was du machen kannst sind:
    - den messding 1000-mal machen und die zeit durch 1000 teilen
    - eine genauere sache als clock() finden und die gibt es, und wie es die gibt! aber halt je nach betriebssystem was spezielles.



  • Kannst du mir vielleicht was "genaueres" verraten? Ich bekomme aus meinem Clock nichts raus - ich werde mal ein paar Zahlen zerlegen, vielleicht messe ich da was.

    Und natürlich vielen Dank für die Info, das war schonmal sehr aufschlussreich :).

    EDIT: Habe gerade nach Primzahlen gesucht, für diese Zahl 260503307319349 kam dann auch tatsächlich ein Ergebnis von clock()! 0.19 Sekunden - ziemlich ungenau, auf jeden Fall nicht genug, wenn ich vorhabe, den Code etwas "zu beschleunigen".
    Bei modernen Computern sollte da schon etwas her, dass ein double an Genauigkeit ausnutzen kann. Wenn ich mir alle Primzahlen unter den ersten 1 Mio. Zahlen in der Konsole ausgeben lasse, dauert das kaum eine Sekunde.



  • Stiefel2000 schrieb:

    Bei modernen Computern sollte da schon etwas her, dass ein double an Genauigkeit ausnutzen kann.

    Das gibt es eventuell bei Startrek oder so.
    Die kleinste double-Zahl ist DBL_MIN = 2.2250738585072014e-308.
    Eine Atomuhr tickert mit rund 1e-15 sec, so eine genaue Stoppuhr wird also schwer zu finden sein.



  • Ich habe ja nicht von der kleinsten Double-Zahl gesprochen - mir ist schon klar, dass man nichts so genau messen kann. Vermutlich ist die Zeit gequantelt und kann ohnehin nicht in kleinere Pakete als vielleicht 10^-50 zerfallen (da hilft also auch kein Star Trek 😉 ).

    Das double, an das ich dachte, wäre ein realistischeres: 1.0000000000000001 z.B., das liegt (hoffentlich) in der double-Genauigkeit und kann trotzdem noch zum Messen großer Zahlen dienen. Die e-308 bekommt man doch nur hin, wenn vor dem Komma eine Null steht. Es wäre ja schon völlig ausreichend, auf 5 oder 6 Nachkommastellen messen zu können - bei einer Taktfrequenz von über 1GHz sollte man mit Hilfe der CPU ganz gut in solche Bereiche vordringen können.



  • Stiefel2000 schrieb:

    Ich habe ja nicht von der kleinsten Double-Zahl gesprochen - mir ist schon klar, dass man nichts so genau messen kann. Vermutlich ist die Zeit gequantelt und kann ohnehin nicht in kleinere Pakete als vielleicht 10^-50 zerfallen (da hilft also auch kein Star Trek 😉 ).

    Das double, an das ich dachte, wäre ein realistischeres: 1.0000000000000001 z.B., das liegt (hoffentlich) in der double-Genauigkeit und kann trotzdem noch zum Messen großer Zahlen dienen. Die e-308 bekommt man doch nur hin, wenn vor dem Komma eine Null steht.

    zeiten kommen aus der hardware immer als ganzzahlen. und das ist auch bis auf weiteres gut so. zeitdifferenzen und zeitvergleiche dauern einen takt.
    jedem steht es frei, für seine anwendung doubles draus zu machen, zum beispiel

    inlien double dclock(){return clock()/double(CLOCKS_PER_SEC)}
    

    du brauchst keinen double, sondern eine clock()-ähnliche funktion, die mehr takte hat.



  • volkard schrieb:

    du brauchst keinen double, sondern eine clock()-ähnliche funktion, die mehr takte hat.

    Einverstanden.



  • sag dann noch schnell, welches betriuebssystem du benutzt. dann wird sich einer finden, der das auch benutzt und dir die spezielle funktion sagen kann.
    oder du willst gleich alle betriebssysteme abdecken, dann wird's bestimmt was in boost geben, was du nehmen kannst.



  • Ich nutze normalerweise: Ubuntu 9.04 64 Bit (steht schon weiter oben), Windows XP SP3 32 Bit oder Windows Vista SP1 64 Bit.

    "Boost" sagt mir allerdings gar nichts, aber natürlich würde ich gern alle Systeme abdecken :).

    EDIT: Ich bin gerade auf die Seite "boost.org" gestoßen (falls du das meintest), ich lese da aber immer nur von C++, ich selbst benutze eigentlich C.



  • Stiefel2000 schrieb:

    EDIT: Ich bin gerade auf die Seite "boost.org" gestoßen (falls du das meintest), ich lese da aber immer nur von C++, ich selbst benutze eigentlich C.

    mist, ich war im falschen forum wildern. ich bin c++.
    linux gettimeofday()
    windows QueryPerformanceCounter()
    beide schaffen mindestens mikrosekunden.
    jetzt kommt auch dein double ins spiel.
    bau

    double getDoubleTime()
    

    und inndnedrun mit

    #idfef __LINUX
    

    oder so, daß es mit win und linux geht und dir einfach nur nen double in sekunden liefert. nur so als vorschlag. 😃



  • 10^-6 also? Das klingt gut, schonmal danke für deine Hilfe.
    Ich gehe jetzt erstmal ins Bett und probiere das morgen mal aus.

    Kann ich das ganze trotzdem einigermaßen allgemein halten? Gibt es zum Beispiel eine Umgebunsvariable, die mir sowas wie "WIN" oder "LINUX" zurückgibt? Dann sollte eine kleine if-Abfrage ja ausreichen.

    EDIT: Da hast du meine Frage ja schon beim Stellen beantwortet - nochmal danke! 🙂



  • Stiefel2000 schrieb:

    Kann ich das ganze trotzdem einigermaßen allgemein halten? Gibt es zum Beispiel eine Umgebunsvariable, die mir sowas wie "WIN" oder "LINUX" zurückgibt? Dann sollte eine kleine if-Abfrage ja ausreichen.

    mußt da #ifdef nehmen. denn unter win mußt du andere header inkludieren als unter linux. also auch schon das #include <windows.h> oder das linuxische pendant stehen in #ifdef.
    der compiler weiß, welches das zielsystem ist und setzt entsprechende makros als definiert. bin jetzt nicht sicher, daß __LINUX das eine da war. irgend wie so ähnlich wars.



  • Stiefel2000 schrieb:

    10^-6 also? Das klingt gut

    nur als anmerkung, falls du spaß am kleinigkeitenoptimieren finden willst:
    mit RDTSC bist du auf 10^-9. unter win einfach mit (wenn ich mich recht erinnere)

    unsigned long long rdtsc(){
       __asm rdtsc;
    }
    

    gibt die seit dem letztren aufruf vergangenen prozessortakte an. allerdings gehts schief, wenn dein thread zwischendurch den prozessor wechselt. und nicht zu vergessen, daß das auslesen selber (bei mir) 26 takte dauert. die mußt du vorher (zur laufzeit, wäre schon netter!) genau ausmessen und dann abziehen.
    RDTSC ist aber gar nicht einfach auszuwerten, weil da so hundsgemein viele störende effekte die zeiten verschmieren.



  • Stiefel2000 schrieb:

    Gibt es zum Beispiel eine Umgebunsvariable, die mir sowas wie "WIN" oder "LINUX" zurückgibt?

    MSVC definiert selbständig WIN32 in den Projekteinstellungen, bei PellesC z.B. musst du das selber eintragen.



  • Ich will jetzt mal die Zeitfunktionen unter Windows und Linux ausprobieren, ich hoffe, dass sich das als nicht zu schwierig herausstellen wird.
    RDTSC klingt natürlich sehr interessant, sollte ich irgendwie damit klarkommen, werde ich das nutzen ;).

    @volkard: Könntest du evt. ein paar Code-Schnipsel angeben, also sozusagen eine grobe Anleitung, wie ich RDTSC nutzen muss?

    Alternativ wäre es auch nett, wenn mir jemand sagen könnte, auf welcher Website man Informationen über die spezifischeren include-Dateien finden kann. Bisher kam ich mit cplusplus.com immer sehr gut klar, aber dort finden sich nur die allgemeineren Informationen. Per google stoße ich auf ziemlich "zersplitterte" Seiten, ein einigermaßen übersichtliches Archiv ist mir noch nicht untergekommen.

    @Hausschuh2009: Visual Studio stünde mir zwar zur Verfügung, aber das Programm ist doch extrem aufgebläht. Für meine bisher sehr kleinen Programme ist mir ein viele GB umfassendes Programm einfach zu groß.
    Ich habe irgendwas von einer abfragbaren PATH-Variable gelesen - vielleicht lässt sich ja darüber das Betriebssystem bestimmen? Immerhin sehen die Pfade unter Windows und Linux verschieden aus.

    EDIT: gettimeofday() gefällt mir sehr gut, Mikrosekundengenau! 🙂 Das Windows-Äquivalent scheint etwas komplizierter zu sein, mal schauen, wie das läuft.

    EDIT2: Ich habe noch mehr mit gettimeofday() getestet und festgestellt, dass ich vorläufig nur Zeiten unter einer Sekunde stoppen kann, danach beginnt der Zähler wieder bei 0. Kann ich das umgehen?

    EDIT3: Ok, das Problem hat sich erledigt - tv_sec und tv_usec speichern verschiedene Werte, mit etwas Rechnerei kann man also auch die ganzen Sekunden unterscheiden.


Anmelden zum Antworten