std::chrono::duration mit variabler ratio



  • Hallo,

    ich habe eine Funktion die ich z.B. exakt 60 mal die Sekunde aufrufen möchte. Aktuell benutze ich diesen Code:

    using Framerate = std::chrono::duration<std::chrono::high_resolution_clock::rep, std::ratio<1, fps>>;
    auto next = std::chrono::high_resolution_clock::now() + Framerate{1};
    while (app->loopIteration())
    {
    	std::this_thread::sleep_until(next);
    	next += Framerate{1};
    }
    

    Das ganze geht aber natürlich nur gut, wenn die Variable fps während der compile time bekannt ist. Nun hier ist der Haken, ich erfahre meine fps erst während der Laufzeit. Wie muss ich meinen Code umschreiben, dass dieser auch mit nicht konstanten fps funktioniert?
    Ich arbeite zum ersten mal mit std::chrono und die Referenz stiftet in diesem Fall leider auch mehr Verwirrung als Erleuchung.

    PS: Der Smiley mit den drei Fragezeichen überm Kopf oder das Clownsgesicht fehlen mir wirklich im neuen Forum. Ansonsten top 👍



  • also allgemein musst du bei solchen sachen die benötigte zeit messen (keine ahnung, wie das mit c++ funktioniert, ich verstehe den code da überhaupt nicht) und die verzögerung dann entsprechend anpassen.

    beispiel: die ausführung deiner funktion benötigt 10ms, das macht dann bei 60 aufrufen 600ms und damit 400ms verzögerungszeit insgesamt bzw. 400/60 = 6,6ms verzögerung pro durchlauf.
    hat die ausführung jetzt 12ms benötigt, brauchst du also nur noch 6,6ms - (12ms - 10ms) = 4,6ms verzögern. hat sie sogar 33ms benötigt, so brauchst du die nächsten 23/6,6 = 3,48 =~3 durchläufe gar nicht zu verzögern und beim 4. durchlauf nur um 6,6 * 0,48 = 3,2ms.



  • @hansklaus sagte in std::chrono::duration mit variabler ratio:

    keine ahnung, wie das mit c++ funktioniert, ich verstehe den code da überhaupt nicht

    Ich will jetzt wirklich nicht unhöflich sein oder dir zu nahe treten. Aber dann lohnt es sich nicht in diesem Thread zu antworten.
    Was ich machen muss ist mir natürlich klar (und tue ich mit dem Code auch schon). Es geht hier eher um die korrekte Anwendung er Standardbibliothek, die mir Probleme bereitet 😉



  • also so wie ich das sehe, misst du die benötigte zeit überhaupt nicht. ich habe jetzt mal für dich gesucht und unter http://www.cplusplus.com/reference/chrono/high_resolution_clock/now/ ein beispiel für eine zeitmessung gefunden. im prinzip musst du das nur anpassen.



  • auto next = std::chrono::high_resolution_clock::now() + Framerate{1};
    

    Was denkst du tut diese Zeile? Bitte lies dir den Eröffnungsbeitrag noch einmal vernünftig und mit beiden Augen durch.
    Zur eigentlichen Frage:
    Hab die Lösung gefunden

    auto framerate = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<double>(1.0 / fps));
    


  • @pikkolini sagte in std::chrono::duration mit variabler ratio:

    Was denkst du tut diese Zeile?
    ehrlich gesagt einen bestimmten zeitraum festlegen, jedoch nicht die tatsächliche zeitspanne zum durchlaufen der funktion ermitteln. wenn du wirklich richtig liegst, sei doch bitte so nett, mir (zu lernzwecken) zu erklären, wo genau du ermittelst, wie lange der funktionsaufruf dauert und wo du die von mir erläuterte anpassung der verzögerung durchführst. wirklich: ich möchte etwas lernen.



  • @hansklaus sagte in std::chrono::duration mit variabler ratio:
    jedoch nicht die tatsächliche zeitspanne zum durchlaufen der funktion ermitteln.

    Das will er doch gar nicht.



  • muss er doch aber. wie will er die funktion sonst exakt 60mal in der sekunde aufrufen?


  • Mod

    @hansklaus sagte in std::chrono::duration mit variabler ratio:

    muss er doch aber. wie will er die funktion sonst exakt 60mal in der sekunde aufrufen?

    So wie gezeigt.

    Umgekehrt würde mich mal interessieren, wie du es machen willst. Gegeben ein Stück Code, das dir sagt, wie lange etwas braucht. Wie machst du nun damit etwas, das eine Funktion 60x in der Sekunde aufruft?



  • naja

    schleife:

     1. zeitpunkt ermitteln
    
     funktion aufrufen
    
     2. zeitpunkt ermitteln
    
     verzögerungszeitraum ausrechnen: 1/60s - (2.zeitpunkt - 1. zeitpunkt)
    
     verzögerung durchführen
    

    schleife ende

    genau das war doch gefragt, oder verstehe ich die deutsche sprache jetzt nicht mehr?



  • @hansklaus sagte in std::chrono::duration mit variabler ratio:

    genau das war doch gefragt, oder verstehe ich die deutsche sprache jetzt nicht mehr?

    Nein, gefragt war wie man aus einer chrono kompatiblen Zeit (keine Ahnung wie ich das nennen soll) die zur compiletime bekannt sein muss, eine chrono kompatible Zeit machen kann, die man erst zur Laufzeit kennen muss. Ist vielleicht blöd formuliert und wer anders kann das besser erklären.
    Mein Code macht folgendes:

    nächster Zeitpunkt = jetzt + 1/60s
    while (...)
        warte bis nächster Zeitpunkt erreicht ist (sleep_until)
        nächster Zeitpunkt = nächster Zeitpunkt + 1/60s
    


  • Möglicherweise ist std::chrono::duration<double> eine Lösung?

    #include <iostream>
    #include <chrono>
    
    int main()
    {
      const auto fps = 50;
      using Framerate = std::chrono::duration<std::chrono::high_resolution_clock::rep, std::ratio<1, fps>>;
      using Framerate2 = std::chrono::duration<double>;
      auto start = std::chrono::high_resolution_clock::now();
      auto end = start + Framerate{1};
      auto end2 = start + Framerate2{ 1. / fps };
      
      std::cout << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << "\n";
      std::cout << std::chrono::duration_cast<std::chrono::microseconds>(end2 - start).count() << "\n";
    }
    


  • @manni66 Danke, das funktioniert auch bestens. Wie ich gerade gemerkt habe ist der duration_cast in meiner Lösung gar nicht nötig.
    Gibt es irgendein Grund eine der beiden folgenden Zeilen vorzuziehen?

    auto framerate = std::chrono::duration<double>{1.0 / fps};
    
    using Framerate = std::chrono::duration<double>
    // und dann Framerate{1.0 / fps} benutzen
    


  • naja, das eine (using) ist einalias des Typs und das andere (auto) ist ein konkretes Objekt.

    Da es in deinem Fall ja einer dauerhaft konstante duration ist, kannst du dir den Umweg über den alias auch sparen; es spricht allerdings auch nichts dagegen einfach beides zu verwenden 😉

    using Framerate = std::chrono::duration<double>;
    auto framerate = Framerate{1.0 / fps};
    

    wobei ich das hier schöner finde:

    using Framerate = std::chrono::duration<double>;
    Framerate framerate{1.0 / fps};
    

Log in to reply