测量线程的上下文切换时间

时间:2016-03-18 05:12:16

标签: c multithreading time

我想计算上下文切换时间,我想使用互斥和条件变量在2个线程之间发出信号,这样一次只能运行一个线程。我可以使用CLOCK_MONOTONIC来衡量整个执行时间,并使用CLOCK_THREAD_CPUTIME_ID来衡量每个线程的运行时间 然后上下文切换时间是(total_time - thread_1_time - thread_2_time)。 为了获得更准确的结果,我可以循环并取平均值。

这是否是接近上下文切换时间的正确方法?我不能想到任何可能出错的事情,但我得到的答案不到1纳秒......

我忘了提到我把它循环的时间越多,取平均值,得到的结果就越小。

修改

这是我的代码片段

    typedef struct
    {
      struct timespec start;
      struct timespec end;
    }thread_time;

    ...


    // each thread function looks similar like this
    void* thread_1_func(void* time)
    {
       thread_time* thread_time = (thread_time*) time;

       clock_gettime(CLOCK_THREAD_CPUTIME_ID, &(thread_time->start)); 
       for(x = 0; x < loop; ++x)
       {
         //where it switches to another thread
       }
       clock_gettime(CLOCK_THREAD_CPUTIME_ID, &(thread_time->end));

       return NULL;
   };

   void* thread_2_func(void* time)
   {
      //similar as above
   }

   int main()
   {
      ...
      pthread_t thread_1;
      pthread_t thread_2;

      thread_time thread_1_time;
      thread_time thread_2_time;

      struct timespec start, end;

      // stamps the start time 
      clock_gettime(CLOCK_MONOTONIC, &start);

      // create two threads with the time structs as the arguments 
      pthread_create(&thread_1, NULL, &thread_1_func, (void*) &thread_1_time);
      pthread_create(&thread_2, NULL, &thread_2_func, (void*) &thread_2_time); 
      // waits for the two threads to terminate 
      pthread_join(thread_1, NULL);
      pthread_join(thread_2, NULL);

      // stamps the end time 
      clock_gettime(CLOCK_MONOTONIC, &end);

      // then I calculate the difference between between total execution time and the total execution time of two different threads..
   }

2 个答案:

答案 0 :(得分:2)

首先,使用CLOCK_THREAD_CPUTIME_ID可能非常错误;此时钟将在用户模式中提供 主题所花费的时间。但是,在用户模式下不会发生上下文切换,您想要使用另一个时钟。此外,在多处理系统上,时钟可以为处理器提供不同的值!因此,我建议您改用CLOCK_REALTIMECLOCK_MONOTONIC。但是请注意,即使您快速连续两次读取其中任何一个,时间戳通常也会相隔数十纳秒。

对于上下文切换 - 有很多种上下文切换。最快的方法是完全用软件从一个线程切换到另一个线程。这只是意味着您将旧寄存器压入堆栈,设置任务切换标志以便SSE / FP寄存器将被延迟保存,保存堆栈指针,加载新堆栈指针并从该函数返回 - 因为另一个线程已经完成相同的操作,该函数的返回发生在另一个线程中。

此线程到线程切换非常快,其开销与任何系统调用大致相同。从一个进程切换到另一个进程要慢得多:这是因为必须通过设置CR0寄存器来刷新和切换用户空间页表。这会导致TLB丢失,将虚拟地址映射到物理地址。

然而&lt; 1 ns上下文切换/系统调用开销似乎并不合理 - 这里很可能存在超线程或2个CPU内核,所以我建议你在该进程上设置CPU亲和力,以便Linux只在第一个CPU核心上运行它:

#include <sched.h>

cpu_set_t  mask;
CPU_ZERO(&mask);
CPU_SET(0, &mask);
result = sched_setaffinity(0, sizeof(mask), &mask);

然后你应该非常确定你测量的时间来自真正的上下文切换。另外,要测量切换浮点/ SSE堆栈的时间(这种情况很懒散),你应该有一些浮点变量并在上下文切换之前对它们进行计算,然后将say .1添加到某个易失性浮点变量上下文切换后查看它是否对切换时间有影响。

答案 1 :(得分:0)

这不是直截了当的,但像往常一样,有人已经做了很多工作。 (我在这里不包括来源,因为我看不到任何提到的许可证)

https://github.com/tsuna/contextswitch/blob/master/timetctxsw.c

如果您将该文件复制到linux机器上(context_switch_time.c),您可以使用此编译并运行它

gcc -D_GNU_SOURCE -Wall -O3 -std=c11 -lpthread context_switch_time.c
./a.out

我在小型虚拟机上获得了以下结果

2000000  thread context switches in 2178645536ns (1089.3ns/ctxsw)

此问题出现之前......对于Linux,您可以在这里找到一些材料。

Write a C program to measure time spent in context switch in Linux OS

注意,当用户在上面的链接中运行测试时,他们也在使用游戏和编译来敲击机器,这就是上下文切换需要很长时间的原因。这里有更多信息......

how can you measure the time spent in a context switch under java platform