Wie erstelle ich einen hochauflösenden Timer in Linux, um die Programmleistung zu messen?

Lesezeit: 9 Minuten

Benutzeravatar von sj755
sj755

Ich versuche, die GPU- mit der CPU-Leistung zu vergleichen. Für die NVIDIA-GPU habe ich die verwendet cudaEvent_t Typen, um ein sehr genaues Timing zu erhalten.

Für die CPU habe ich folgenden Code verwendet:

// Timers
clock_t start, stop;
float elapsedTime = 0;

// Capture the start time

start = clock();

// Do something here
.......

// Capture the stop time
stop = clock();
// Retrieve time elapsed in milliseconds
elapsedTime = (float)(stop - start) / (float)CLOCKS_PER_SEC * 1000.0f;

Anscheinend ist dieser Code nur dann gut, wenn Sie in Sekunden zählen. Außerdem kommen die Ergebnisse manchmal ziemlich seltsam heraus.

Kennt jemand eine Möglichkeit, einen hochauflösenden Timer unter Linux zu erstellen?

  • Siehe diese Frage: stackoverflow.com/questions/700392/…

    – Steve-o

    19. Juli 2011 um 15:26 Uhr

Benutzeravatar von NPE
NPE

Kasse clock_gettimedas eine POSIX-Schnittstelle zu hochauflösenden Timern ist.

Wenn Sie sich nach dem Lesen der Manpage über den Unterschied zwischen wundern CLOCK_REALTIME und CLOCK_MONOTONICsiehe Unterschied zwischen CLOCK_REALTIME und CLOCK_MONOTONIC?

Auf der folgenden Seite finden Sie ein vollständiges Beispiel: http://www.guyrutenberg.com/2007/09/22/profiling-code-using-clock_gettime/

#include <iostream>
#include <time.h>
using namespace std;

timespec diff(timespec start, timespec end);

int main()
{
    timespec time1, time2;
    int temp;
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1);
    for (int i = 0; i< 242000000; i++)
        temp+=temp;
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time2);
    cout<<diff(time1,time2).tv_sec<<":"<<diff(time1,time2).tv_nsec<<endl;
    return 0;
}

timespec diff(timespec start, timespec end)
{
    timespec temp;
    if ((end.tv_nsec-start.tv_nsec)<0) {
        temp.tv_sec = end.tv_sec-start.tv_sec-1;
        temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
    } else {
        temp.tv_sec = end.tv_sec-start.tv_sec;
        temp.tv_nsec = end.tv_nsec-start.tv_nsec;
    }
    return temp;
}

  • Nur damit ich klar bin, was ich gelesen habe, können Sie mir ein Beispiel geben, wie Sie clock_gettime verwenden würden, um die verstrichene Zeit in Nanosekunden zu finden?

    – sj755

    19. Juli 2011 um 16:05 Uhr

  • @seljuq70: Ich habe einen Link zu einem vollständigen Beispiel hinzugefügt.

    – NPE

    19. Juli 2011 um 17:01 Uhr

  • OP wurde C geschrieben, aber Ihre Antwort ist C++. Immer noch nützlich, aber nicht auf meinem ZedBoard, das keine C++-Bibliotheken hat 😀 Um das zu beheben, stellen Sie der Zeitangabe struct voran und entfernen Sie die Couts.

    – Eule

    6. August 2018 um 10:32 Uhr


  • Die Antwort spricht also ausdrücklich von CLOCK_REALTIME und CLOCK_MONOTONIC aber wir enden mit CLOCK_PROCESS_CPUTIME_ID im Codebeispiel? Kann das jemand klären? Was soll gehen?

    – itMaxence

    30. August 2018 um 10:05 Uhr


  • @itMaxence Schauen Sie sich das an: stackoverflow.com/a/3527632/9732482

    – jplozier

    10. Juni 2021 um 13:48 Uhr

Benutzeravatar von Alex
Alex

Um die bisher vorgestellten Informationen zusammenzufassen, sind dies die beiden Funktionen, die für typische Anwendungen erforderlich sind.

#include <time.h>

// call this function to start a nanosecond-resolution timer
struct timespec timer_start(){
    struct timespec start_time;
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_time);
    return start_time;
}

// call this function to end a timer, returning nanoseconds elapsed as a long
long timer_end(struct timespec start_time){
    struct timespec end_time;
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_time);
    long diffInNanos = (end_time.tv_sec - start_time.tv_sec) * (long)1e9 + (end_time.tv_nsec - start_time.tv_nsec);
    return diffInNanos;
}

Hier ist ein Beispiel dafür, wie man sie beim Timing verwendet, wie lange es dauert, die Varianz einer Eingabeliste zu berechnen.

struct timespec vartime = timer_start();  // begin a timer called 'vartime'
double variance = var(input, MAXLEN);  // perform the task we want to time
long time_elapsed_nanos = timer_end(vartime);
printf("Variance = %f, Time taken (nanoseconds): %ld\n", variance, time_elapsed_nanos);

  • Ignorierst du nicht die tv_sec des timespec? Auch warum CLOCK_PROCESS_CPUTIME_ID statt CLOCK_MONOTONIC?

    – Amaurea

    28. Februar 2014 um 17:37 Uhr

  • Das Poster vergleicht die CPU- mit der GPU-Leistung. Sie geben ehrlich Code, der die CPU-Zeit erhält. CLOCK_PROCESS_CPUTIME_ID. Dies bedeutet, dass er um viele Größenordnungen beschleunigt wird. Verwenden Sie für die CPU / GPU-Leistung (diese Frage) immer die Wandzeit. Entfernen Sie diese Antwort.

    – Tim Zaman

    22. Juni 2016 um 12:15 Uhr

  • @TimZaman Ja, Echtzeit könnte im Anwendungsfall des Posters besser sein. Ich werde jedoch keine Antwort notieren, offensichtlich fanden die Leute es nützlich. Prost.

    – Alex

    23. Juni 2016 um 8:29 Uhr

  • Bevor Sie CLOCK_PROGRESS_CPUTIME_ID verwenden, sollten Sie ausführen grep constant_tsc /proc/cpuinfo um zu verstehen, wie diese Uhr funktioniert. Wenn Sie CPU nicht unterstützt konstant_tsc, spiegelt die Zeit die tatsächlichen CPU-Taktzyklen wider. Wenn das Flag gesetzt ist, wird die Uhr angepasst, um die aktuelle CPU-Frequenz zu berücksichtigen. Ich gebe diesem eine -1, weil time_elapsed_nanos wird falsch berechnet. Dies vielleicht ein besserer Ansatz.

    – Fredk

    16. August 2016 um 14:41 Uhr


struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);

es gibt auch CLOCK_REALTIME_HR, aber ich bin mir nicht sicher, ob es einen Unterschied macht.

  • Und ich bin mir nicht sicher, ob CLOCK_REALTIME_HR wird unterstützt. Frage.

    – Gsamaras

    7. September 2017 um 11:13 Uhr

Interessieren Sie sich für Wall Time (wie viel Zeit vergeht tatsächlich) oder Cycle Count (wie viele Zyklen)? Im ersten Fall sollten Sie etwas wie verwenden gettimeofday.

Der Timer mit der höchsten Auflösung verwendet die RDTSC x86 Montageanleitung. Dies misst jedoch die Taktzeiten, daher sollten Sie sicher sein, dass der Energiesparmodus deaktiviert ist.

Die Wiki-Seite für TSC gibt einige Beispiele: http://en.wikipedia.org/wiki/Time_Stamp_Counter

Benutzeravatar von radato
radato

Nachdem ich diesen Thread gelesen hatte, begann ich, den Code für clock_gettime gegen den Chrono von c++ 11 zu testen, und sie scheinen nicht übereinzustimmen.

Dazwischen klafft eine riesige Lücke!

Das std::chrono::Sekunden(1) scheint gleichwertig zu sein ~70.000 des clock_gettime

#include <ctime>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <thread>
#include <chrono>
#include <iomanip>
#include <vector>
#include <mutex>

timespec diff(timespec start, timespec end);
timespec get_cpu_now_time();
std::vector<timespec> get_start_end_pairs();
std::vector<timespec> get_start_end_pairs2();
void output_deltas(const std::vector<timespec> &start_end_pairs);

//=============================================================
int main()
{
    std::cout << "Hello waiter" << std::endl; // flush is intentional
    std::vector<timespec> start_end_pairs = get_start_end_pairs2();
    output_deltas(start_end_pairs);

    return EXIT_SUCCESS;
}

//=============================================================
std::vector<timespec> get_start_end_pairs()
{
    std::vector<timespec> start_end_pairs;
    for (int i = 0; i < 20; ++i)
    {
        start_end_pairs.push_back(get_cpu_now_time());
        std::this_thread::sleep_for(std::chrono::seconds(1));
        start_end_pairs.push_back(get_cpu_now_time());
    }

    return start_end_pairs;
}


//=============================================================
std::vector<timespec> get_start_end_pairs2()
{
    std::mutex mu;
    std::vector<std::thread> workers;
    std::vector<timespec> start_end_pairs;
    for (int i = 0; i < 20; ++i) {
        workers.emplace_back([&]()->void {
            auto start_time = get_cpu_now_time();
            std::this_thread::sleep_for(std::chrono::seconds(1));
            auto end_time = get_cpu_now_time();
            std::lock_guard<std::mutex> locker(mu);
            start_end_pairs.emplace_back(start_time);
            start_end_pairs.emplace_back(end_time);
        });
    }

    for (auto &worker: workers) {
        if (worker.joinable()) {
            worker.join();
        }
    }

    return start_end_pairs;
}

//=============================================================
void output_deltas(const std::vector<timespec> &start_end_pairs)
{
    std::cout << "size: " << start_end_pairs.size() << std::endl;
    for (auto it_start = start_end_pairs.begin(); it_start < start_end_pairs.end(); it_start += 2)
    {
        auto it_end = it_start + 1;
        auto delta = diff(*it_start, *it_end);

        std::cout
                << std::setw(2)
                << std::setfill(' ')
                << std::distance(start_end_pairs.begin(), it_start) / 2
                << " Waited ("
                << delta.tv_sec
                << "\ts\t"
                << std::setw(9)
                << std::setfill('0')
                << delta.tv_nsec
                << "\tns)"
                << std::endl;
    }
}

//=============================================================
timespec diff(timespec start, timespec end)
{
    timespec temp;
    temp.tv_sec = end.tv_sec-start.tv_sec;
    temp.tv_nsec = end.tv_nsec-start.tv_nsec;

    if (temp.tv_nsec < 0) {
        --temp.tv_sec;
        temp.tv_nsec += 1000000000;
    }
    return temp;
}

//=============================================================
timespec get_cpu_now_time()
{
    timespec now_time;
    memset(&now_time, 0, sizeof(timespec));
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &now_time);

    return now_time;
}

Ausgang:

Hello waiter
 0 Waited (0    s       000843254       ns)
 1 Waited (0    s       000681141       ns)
 2 Waited (0    s       000685119       ns)
 3 Waited (0    s       000674252       ns)
 4 Waited (0    s       000714877       ns)
 5 Waited (0    s       000624202       ns)
 6 Waited (0    s       000746091       ns)
 7 Waited (0    s       000575267       ns)
 8 Waited (0    s       000860157       ns)
 9 Waited (0    s       000827479       ns)
10 Waited (0    s       000612959       ns)
11 Waited (0    s       000534818       ns)
12 Waited (0    s       000553728       ns)
13 Waited (0    s       000586501       ns)
14 Waited (0    s       000627116       ns)
15 Waited (0    s       000616725       ns)
16 Waited (0    s       000616507       ns)
17 Waited (0    s       000641251       ns)
18 Waited (0    s       000683380       ns)
19 Waited (0    s       000850205       ns)

  • Ich denke ++temp.tv_sec; ist ein Typ und Sie meinten –temp.tv_sec; in der diff-Funktion.

    – Simone-Cu

    21. März um 17:27 Uhr

  • Es ist kein Typ, wenn ich die 2 Strukturen subtrahiere, berücksichtige ich, dass es einen geben könnte Übertrag

    – radato

    22. März um 8:53 Uhr


  • Ja, das verstanden. Aber wenn Sie den Übertrag von s auf ns durchführen, sollten Sie 1 vom Sekundenfeld subtrahieren und 1000000000 (1s) zum ns-Feld addieren. Sagen wir (10s and 900ns) - (5s and 1000ns) –> 5s and -100ns –> 4s and (-100+10^9)ns. Der letzte Schritt verringert die Sekunde und führt so den Übertrag aus.

    – Simone-Cu

    23. März um 15:42 Uhr


  • Ja richtig, ich habe die Antwort entsprechend korrigiert

    – radato

    27. März um 5:11 Uhr

Benutzeravatar von Nikolai Fetissov
Nikolai Fetissov

clock_gettime(2)

  • Ich denke ++temp.tv_sec; ist ein Typ und Sie meinten –temp.tv_sec; in der diff-Funktion.

    – Simone-Cu

    21. März um 17:27 Uhr

  • Es ist kein Typ, wenn ich die 2 Strukturen subtrahiere, berücksichtige ich, dass es einen geben könnte Übertrag

    – radato

    22. März um 8:53 Uhr


  • Ja, das verstanden. Aber wenn Sie den Übertrag von s auf ns durchführen, sollten Sie 1 vom Sekundenfeld subtrahieren und 1000000000 (1s) zum ns-Feld addieren. Sagen wir (10s and 900ns) - (5s and 1000ns) –> 5s and -100ns –> 4s and (-100+10^9)ns. Der letzte Schritt verringert die Sekunde und führt so den Übertrag aus.

    – Simone-Cu

    23. März um 15:42 Uhr


  • Ja richtig, ich habe die Antwort entsprechend korrigiert

    – radato

    27. März um 5:11 Uhr

epoll-Implementierung:
https://github.com/ielife/simple-timer-for-c-language

so verwenden:

timer_server_handle_t *timer_handle = timer_server_init(1024);
if (NULL == timer_handle) {
    fprintf(stderr, "timer_server_init failed\n");
    return -1;
}
ctimer timer1;
    timer1.count_ = 3;
    timer1.timer_internal_ = 0.5;
    timer1.timer_cb_ = timer_cb1;
    int *user_data1 = (int *)malloc(sizeof(int));
    *user_data1 = 100;
    timer1.user_data_ = user_data1;
    timer_server_addtimer(timer_handle, &timer1);

    ctimer timer2;
    timer2.count_ = -1;
    timer2.timer_internal_ = 0.5;
    timer2.timer_cb_ = timer_cb2;
    int *user_data2 = (int *)malloc(sizeof(int));
    *user_data2 = 10;
    timer2.user_data_ = user_data2;
    timer_server_addtimer(timer_handle, &timer2);

    sleep(10);

    timer_server_deltimer(timer_handle, timer1.fd);
    timer_server_deltimer(timer_handle, timer2.fd);
    timer_server_uninit(timer_handle);

1407160cookie-checkWie erstelle ich einen hochauflösenden Timer in Linux, um die Programmleistung zu messen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy