我正在尝试“粗略地”计算 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 对我尝试测量的内容不够精确,还是我忽略了其他一些问题造成的?
最佳答案
i see that tv_nsec seems to be a 'rounded' number as well:
2626, 714885000, 2626, 714888000
Would this be caused by a clock_monotonic not being precise enough for what im attempting to measure, or some other problem that i'm overlooking?
是的,这是可能的。系统支持的每个时钟都有固定的分辨率。 struct timespec
能够支持具有纳秒分辨率的时钟,但这并不意味着您可以期望每个时钟实际上都具有这样的分辨率。看起来您的 CLOCK_MONOTONIC
可能具有 1 微秒(1000 纳秒)的分辨率,但您可以通过 clock_getres()
函数进行检查。
如果它可用,那么您可以尝试 CLOCK_PROCESS_CPUTIME_ID
。对于您来说,这可能比 CLOCK_MONOTONIC
具有更高的分辨率,但请注意,单微秒的分辨率非常精确——在现代机器上大约是每 3000 个 CPU 周期一个滴答。
即便如此,我发现您的方法存在几个可能的问题:
虽然您将进程设置为对单个 CPU 具有亲和性,但这并不妨碍系统在该 CPU 上也调度其他进程。因此,除非您采取了额外的措施,否则您无法确定(甚至不太可能)从您的一个程序线程切换的每个上下文都到另一个线程。
您启动第二个线程,然后立即加入它。之后你的线程之间不再有上下文切换,因为你的第二个线程在成功加入后不再存在。
计数为 0 的 read()
可能会或可能不会检查错误,并且它当然不会传输任何数据。我完全不清楚为什么您将调用的时间与上下文切换的时间标识在一起。如果上下文切换确实发生在您正在计时的空间中,那么至少需要发生两次 - 离开您的程序并返回到它。此外,您还要测量在其他上下文中运行的任何其他内容所消耗的时间,而不仅仅是切换时间。因此,1000 纳秒的步长可能反射(reflect)时间片,而不是切换时间。
您的主线程正在将空字符写入管道的写入端,但似乎没有任何内容在读取它们。如果确实没有那么这最终将填满管道的缓冲区和 block 。我迷失了目标。
关于c - 上下文切换场景下clock_gettime()的准确性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54447430/