上下文切换方案中clock_gettime()的准确性

时间:2019-01-30 18:41:33

标签: c multithreading posix context-switch

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

我已经实现了sched_setaffinity(),以便仅使程序在内核0上运行。我试图在代码中保留尽可能多的绒毛,以便仅测量上下文切换的时间,因此踩进程仅写入管道中有一个字符,父节点读取0字节。

我有一个父级踏步,它创建一个子线程,并在它们之间使用单向管道传递数据,该子线程运行一个简单的函数以写入管道。

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

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

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不够精确而无法即时测量,还是我忽略了其他一些问题?

1 个答案:

答案 0 :(得分:1)

  

我看到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纳秒的步长可能反映的是时间片,而不是切换时间。

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