【问题标题】:Measuring execution time when using threads使用线程时测量执行时间
【发布时间】:2019-10-03 20:22:42
【问题描述】:

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

我想问是否有更好的解决方案。

谢谢。

【问题讨论】:

  • 仅供参考:易失性不是原子的,如果这完全可行,那基本上是运气。
  • 这里有一个种族问题。如果工作线程足够快,EventHandler 可能会在首次初始化t 之前被调用...您最好不要尝试跨线程时间测量。这只是随机的。

标签: c++ multithreading time measure


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    相反,您必须使用特定于平台的 API,例如用于 POSIX 兼容系统的 pthread_getcpuclockid(检查是否定义了 _POSIX_THREAD_CPUTIME),它计算特定线程所花费的实际时间。

    您可以查看我为支持线程感知测量的 C++ 编写的 benchmarking library(参见 struct thread_clock 实现)。

    或者,您可以使用man page中的代码sn-p:

    /* 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 */
    }
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 2016-06-11
        • 2011-02-16
        • 1970-01-01
        • 2021-05-05
        • 1970-01-01
        • 1970-01-01
        • 2013-06-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多