it-swarm.com.de

So erstellen Sie einen hochauflösenden Timer in Linux, um die Programmleistung zu messen

Ich versuche, die GPU mit der CPU-Leistung zu vergleichen. Für die NVIDIA-GPU habe ich die cudaEvent_t-Typen verwendet, 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 gut, wenn Sie in Sekunden zählen. Auch kommen die Ergebnisse manchmal ziemlich seltsam heraus.

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

31
sj755

Check out clock_gettime , welches eine POSIX-Schnittstelle für hochauflösende Timer ist.

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

Ein vollständiges Beispiel finden Sie auf der folgenden Seite: 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;
}
41
NPE

Um die bisher präsentierten Informationen zusammenzufassen, sind dies die zwei 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 ein Beispiel, wie sie im Timing verwendet werden können, wie lange es dauert, um 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);
18
Alex
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);

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

1
Karoly Horvath

Interessieren Sie sich für die Wandzeit (wie viel Zeit vergeht tatsächlich) oder die Anzahl der Zyklen (wie viele Zyklen)? Im ersten Fall sollten Sie etwas wie gettimeofday verwenden.

Der Timer mit der höchsten Auflösung verwendet die Assembly-Anweisung RDTSC x86. Dies misst jedoch die Zeittakte. Sie sollten also sicherstellen, dass der Energiesparmodus deaktiviert ist.

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

1
Foo Bah
0

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 zu passen.

Es gibt eine große Lücke zwischen ihnen!

Die std :: chrono :: Sekunden (1) scheint äquivalent zu ~ 30.000 der clock_gettime  

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

timespec diff(timespec start, timespec end);
timespec get_cpu_now_time();
std::vector<timespec> get_start_end_pairs();
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_pairs();
    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;
}

//=============================================================
void output_deltas(const std::vector<timespec> &start_end_pairs)
{
    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
            << "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;
}

ausgabe:

Waited (0   s   000064802   ns)
Waited (0   s   000028512   ns)
Waited (0   s   000030664   ns)
Waited (0   s   000041233   ns)
Waited (0   s   000013458   ns)
Waited (0   s   000024068   ns)
Waited (0   s   000027591   ns)
Waited (0   s   000028148   ns)
Waited (0   s   000033783   ns)
Waited (0   s   000022382   ns)
Waited (0   s   000027866   ns)
Waited (0   s   000028085   ns)
Waited (0   s   000028012   ns)
Waited (0   s   000028172   ns)
Waited (0   s   000022121   ns)
Waited (0   s   000052940   ns)
Waited (0   s   000032138   ns)
Waited (0   s   000028082   ns)
Waited (0   s   000034486   ns)
Waited (0   s   000018875   ns)
0
radato

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

verwenden Sie wie folgt:

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);
0
Kevin Lee