首页 > 解决方案 > 上下文切换场景中clock_gettime()的准确性

问题描述

我正在尝试“粗略”计算 Linux 系统中线程上下文切换的时间。我编写了一个使用管道和多线程来实现此目的的程序。运行程序时,计算的时间显然是错误的(见下面的输出)。我不确定这是否是由于我在此过程中使用了错误的 clock_id 或者我的实现

我已经实现了 sched_setaffinity() 以便只让程序在核心 0 上运行。我试图在代码中留下尽可能多的绒毛,以便仅测量上下文切换的时间,因此胎面过程只写一个字符到管道,父级读取 0 字节。

我有一个父线程,它创建一个子线程,它们之间有一个单向管道来传递数据,子线程运行一个简单的函数来写入管道。

    void* thread_1_function()
    {
         write(fd2[1],"",sizeof("");
    }

当父线程创建子线程时,启动时间计数器,然后在子线程写入的管道上调用读取。

int main(int argc, char argv[])
{
//time struct declaration
struct timespec start,end;

//sets program to only use core 0
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(0,&cpu_set);


if((sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set) < 1))
{

int nproc = sysconf(_SC_NPROCESSORS_ONLN);
int k;

printf("Processor used: ");
for(k = 0; k < nproc; ++k)
{
    printf("%d ", CPU_ISSET(k, &cpu_set));
}

printf("\n");


if(pipe(fd1) == -1)
{
    printf("fd1 pipe error");
    return 1;
}
//fail on file descriptor 2 fail
if(pipe(fd2) == -1)
{
    printf("fd2 pipe error");
    return 1;
}


pthread_t thread_1;


pthread_create(&thread_1, NULL, &thread_1_function, NULL);


pthread_join(thread_1,NULL);


int i;
uint64_t sum = 0;

for(i = 0; i < iterations; ++i)
{

    //initalize clock start
    clock_gettime(CLOCK_MONOTONIC, &start);
    //wait for child thread to write to pipe
    read(fd2[0],input,0);
    //record clock end
    clock_gettime(CLOCK_MONOTONIC, &end);   

    write(fd1[1],"",sizeof(""));



    uint64_t diff;
    diff = billion * (end.tv_sec - start.tv_sec) + end.tv_nsec - start.tv_nsec;
    diff = diff;
    sum += diff;
}

我在运行时得到的结果通常是这样的:

     3000
     3000
     4000
     2000
     12000
     3000
     5000

依此类推,当我检查返回到开始和结束 timespec 结构的时间时,我看到 tv_nsec 似乎也是一个“四舍五入”的数字:

     start.tv_nsec: 714885000, end.tv_nsec: 714888000

这会是由于clock_monotonic 对于我试图测量的内容不够精确,还是因为我忽略了其他一些问题?

标签: cmultithreadingposixcontext-switch

解决方案


我看到 tv_nsec 似乎也是一个“四舍五入”的数字:

 2626, 714885000, 2626, 714888000

这会是由于clock_monotonic 对于我试图测量的内容不够精确,还是因为我忽略了其他一些问题?

是的,这是一种可能。系统支持的每个时钟都有固定的分辨率。struct timespec能够支持具有纳秒分辨率的时钟,但这并不意味着您可以期望每个时钟实际上都具有这样的分辨率。看起来您CLOCK_MONOTONIC的分辨率可能为 1 微秒(1000 纳秒),但您可以通过该clock_getres()功能进行检查。

如果您可以使用它,那么您可以尝试CLOCK_PROCESS_CPUTIME_ID. 它可能具有比CLOCK_MONOTONIC您更高的分辨率,但请注意,单微秒分辨率非常精确 - 在现代机器上每 3000 个 CPU 周期大约一个滴答。


即便如此,我还是看到您的方法存在几个可能的问题:

  • 尽管您将进程设置为对单个 CPU 具有亲和力,但这也不会阻止系统在该 CPU 上调度其他进程。因此,除非您采取了额外的措施,否则您无法确定——甚至不太可能——每个上下文从程序的一个线程切换另一个线程。

  • 你开始你的第二个线程,然后立即加入它。之后您的线程之间不再有上下文切换,因为您的第二个线程在成功加入后不再存在。

  • read()计数为 0 可能会或可能不会检查错误,它当然不会传输任何数据。我完全不清楚为什么你会用上下文切换的时间来确定那个调用的时间。

  • 如果在您正在计时的空间中确实发生了上下文切换,那么至少需要在那里发生两次——远离您的程序并返回到它。此外,您还要测量在其他上下文中运行的任何其他内容所消耗的时间,而不仅仅是切换时间。因此,1000 纳秒的步长可以反映时间片,而不是切换时间。

  • 您的主线程正在将空字符写入管道的写入端,但似乎没有任何内容读取它们。如果确实没有,那么这最终将填满管道的缓冲区并阻塞。我失去了目的。


推荐阅读