Измерение времени выполнения при использовании потоков - PullRequest
1 голос
/ 16 мая 2019

Я хотел бы измерить время выполнения некоторого кода. Код начинается в функции main () и заканчивается в обработчике событий.

У меня есть код C ++ 11, который выглядит следующим образом:

#include <iostream>

#include <time.h>

...

volatile clock_t t;

void EventHandler()
{
    // when this function called is the end of the part that I want to measure
    t = clock() - t;
    std::cout << "time in seconds: " << ((float)t)/CLOCKS_PER_SEC;
}

int main()
{
    MyClass* instance = new MyClass(EventHandler); // this function starts a new std::thread
    instance->start(...); // this function only passes some data to the thread working data, later the thread will call EventHandler()
    t = clock();
    return 0;
}

Таким образом, гарантируется, что EventHandler () будет вызываться только один раз и только после вызова instance-> start ().

Работает, этот код выводит меня, но это ужасный код, он использует глобальную переменную и разные потоки обращаются к глобальной переменной. Однако я не могу изменить используемый API (конструктор, способ, которым поток вызывает EventHandler).

Я хотел бы спросить, существует ли лучшее решение.

Спасибо.

Ответы [ 3 ]

1 голос
/ 16 мая 2019

Глобальная переменная неизбежна, поскольку MyClass ожидает простую функцию и нет способа передать некоторый указатель контекста вместе с функцией ...

Вы можете написать код в несколько более аккуратном видеКстати, хотя:

#include <future>
#include <thread>
#include <chrono>
#include <iostream>

struct MyClass
{
    typedef void (CallbackFunc)();

    constexpr explicit MyClass(CallbackFunc* handler)
     : m_handler(handler)
    {
    }

    void Start()
    {
        std::thread(&MyClass::ThreadFunc, this).detach();
    }

private:
    void ThreadFunc()
    {
        std::this_thread::sleep_for(std::chrono::seconds(5));
        m_handler();
    }

    CallbackFunc*     m_handler;
};


std::promise<std::chrono::time_point<std::chrono::high_resolution_clock>>   gEndTime;

void EventHandler()
{
    gEndTime.set_value(std::chrono::high_resolution_clock::now());
}


int main()
{
    MyClass         task(EventHandler);

    auto            trigger = gEndTime.get_future();
    auto            startTime = std::chrono::high_resolution_clock::now();
    task.Start();
    trigger.wait();

    std::chrono::duration<double>   diff = trigger.get() - startTime;

    std::cout << "Duration = " << diff.count() << " secs." << std::endl;

    return 0;
}
0 голосов
/ 16 мая 2019

clock() здесь не тот инструмент, потому что он не учитывает время, фактически требуемое ЦПУ для выполнения вашей операции, например, если поток вообще не работает, время все еще считается.

Вместо этого вы должны использовать специфичные для платформы API, такие как pthread_getcpuclockid для POSIX-совместимых систем (проверьте, определено ли _POSIX_THREAD_CPUTIME), которое подсчитывает фактическое время, потраченное конкретным потоком.

Вы можете взглянуть на библиотеку бенчмаркинга , которую я написал для C ++, которая поддерживает измерение с учетом потоков (см. struct thread_clock реализация).

Или вы можете использоватьфрагмент кода из справочной страницы :

/* Link with "-lrt" */

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>

#define handle_error(msg) \
        do { perror(msg); exit(EXIT_FAILURE); } while (0)

#define handle_error_en(en, msg) \
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

static void *
thread_start(void *arg)
{
    printf("Subthread starting infinite loop\n");
    for (;;)
        continue;
}

static void
pclock(char *msg, clockid_t cid)
{
    struct timespec ts;
    printf("%s", msg);
    if (clock_gettime(cid, &ts) == -1)
        handle_error("clock_gettime");
    printf("%4ld.%03ld\n", ts.tv_sec, ts.tv_nsec / 1000000);
}

int
main(int argc, char *argv[])
{
    pthread_t thread;
    clockid_t cid;
    int j, s;

    s = pthread_create(&thread, NULL, thread_start, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_create");

    printf("Main thread sleeping\n");
    sleep(1);

    printf("Main thread consuming some CPU time...\n");
    for (j = 0; j < 2000000; j++)
        getppid();

    pclock("Process total CPU time: ", CLOCK_PROCESS_CPUTIME_ID);

    s = pthread_getcpuclockid(pthread_self(), &cid);
    if (s != 0)
        handle_error_en(s, "pthread_getcpuclockid");

    pclock("Main thread CPU time:   ", cid);

    /* The preceding 4 lines of code could have been replaced by:
    pclock("Main thread CPU time:   ", CLOCK_THREAD_CPUTIME_ID); */

    s = pthread_getcpuclockid(thread, &cid);
    if (s != 0)
        handle_error_en(s, "pthread_getcpuclockid");
    pclock("Subthread CPU time: 1    ", cid);

    exit(EXIT_SUCCESS);         /* Terminates both threads */
}
0 голосов
/ 16 мая 2019
Вызов

clock () не отфильтровывает выполнения различных процессов и потоков, запускаемых планировщиком параллельно с потоком обработчика событий программы. Есть альтернатива, например, times () и getrusage (), которая сообщает процессору время процесса. Хотя для этих вызовов явно не упоминается поведение потоков, но если это Linux, потоки рассматриваются как процессы, но это необходимо исследовать.

...