使用线程时测量执行时间

Measuring execution time when using threads

我想测量一些代码的执行时间。代码从 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 的方式)。

请问有没有更好的解决办法

谢谢。

clock() 调用不会过滤掉与程序事件处理程序线程并行的调度程序 运行 不同进程和线程的执行。还有像 times() 和 getrusage() 这样的替代方法,它告诉 cpu 处理时间。虽然没有明确提及这些调用的线程行为,但如果是 Linux,线程将被视为进程,但必须对其进行调查。

clock() 在这里是错误的工具,因为它不计算你的操作 CPU 到 运行 实际需要的时间,例如,如果线程不是 运行宁都没有,时间还在算。

相反,您必须使用 platform-specific API,例如 POSIX-compliant 系统的 pthread_getcpuclockid(检查是否定义了 _POSIX_THREAD_CPUTIME),它计算了实际花费的时间一个特定的线程。

您可以看一下我为支持 thread-aware 测量的 C++ 编写的 benchmarking library(参见 struct thread_clock 实现)。

或者,您可以使用 man page:

中的代码片段
/* 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 */
}

全局变量是不可避免的,只要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;
}