我正在编写一些代码来粗略地测量上下文切换的成本。基本思想在教科书里面OSTEP 。基于这个想法,我写了一些代码如下:
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <unistd.h>
#include <time.h>
#define TIMES 1000
#define BILLION 10e9
int main(int argc, char *argv[]) {
int pipefd_1[2], pipefd_2[2];
struct timespec start, stop;
clockid_t clk_id = CLOCK_REALTIME;
// for child and parent process run on the same cpu
cpu_set_t set;
int parentCPU, childCPU;
char testChar = 'a'; /* Use for test */
if (argc != 3) {
fprintf(stderr, "Usage: %s parent-cpu child-cpu\n",
argv[0]);
exit(EXIT_FAILURE);
}
parentCPU = atoi(argv[1]);
childCPU = atoi(argv[2]);
CPU_ZERO(&set);
if (pipe(pipefd_1) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
if (pipe(pipefd_2) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
switch (fork()) {
case -1: /* error */
perror("fork");
exit(EXIT_FAILURE);
case 0: /* child process */
CPU_SET(childCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) {
perror("set cpu for child process");
exit(EXIT_FAILURE);
}
char readChar_c;
close(pipefd_1[0]); /* Close unused read end */
close(pipefd_2[1]); /* Close unused write end */
for (int i = 0; i < TIMES; ++i) {
while (read(pipefd_2[0], &readChar_c, 1) <= 0) {} /* read to the first pipe */
write(pipefd_1[1], &readChar_c, 1); /* write to the first pipe */
}
close(pipefd_2[0]);
close(pipefd_1[1]);
exit(EXIT_SUCCESS);
default: /* parent process */
CPU_SET(parentCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) {
perror("set cpu for parent process");
exit(EXIT_FAILURE);
}
char readChar_p;
close(pipefd_2[0]); /* Close unused read end */
close(pipefd_1[1]); /* Close unused write end */
clock_gettime(clk_id, &start);
for (int i = 0; i < TIMES; ++i) {
write(pipefd_2[1], &testChar, 1); /* write to the second pipe */
while (read(pipefd_1[0], &readChar_p, 1) <= 0) {} /* read to the first pipe */
}
clock_gettime(clk_id, &stop);
close(pipefd_2[1]);
close(pipefd_1[0]);
printf("the average cost of context switching is: %lf nsec\n", ((stop.tv_sec - start.tv_sec) * BILLION
+ stop.tv_nsec - start.tv_nsec) / TIMES);
}
exit(EXIT_SUCCESS);
}
但我对这个问题仍然有一些疑问。
我看过别人的代码,他们只是使用read(pipefd_2[0], NULL, 0)和write(pipefd_1[1], NULL, 0)来执行读写操作。我不确定如果你在一个进程中还没有向pipe1写入一些数据,而你想在其他进程中通过pipe1读取数据,这种情况会发生上下文切换吗?或者 read 函数只返回 0?
由于通过管道读取会发生上下文切换,因此上下文切换的精确成本应该是从离开该进程到进入另一个进程,而不包括在后面的进程中执行某些指令的时间,所以我认为用这种方式来计算上下文切换的成本可能不够精确。这是因为与切换上下文相比,执行时间可以忽略不计吗?
感谢您的帮助!
最佳答案
#define BILLION 1e9//不是 10e9
代码没问题。如果管道中没有数据,
read()
不会返回 0,它会阻塞。这就是为什么你正在做的乒乓球可以有效地衡量成本 上下文切换(+IO 开销)。
read()
仅当所有操作系统计数的引用(已创建)时,才为管道的读取端返回 0 通过 dup* 函数或与 fd 继承相结合的 fork ) 到相应的写端都是关闭的。您正在有效地测量上下文切换 + 管道的 IO 开销。您可以通过调整代码以在 >=2 核心系统上仅使用一个管道(因此每个 io 调用几乎没有上下文切换)并使一个进程成为永久读取器,使另一个进程成为永久读取器,从而单独测量管道的近似 IO 开销作家(https://pastebin.com/cGDWFdgQ)。我得到了大约 2*0.55μs 的开销 + 整个过程大约 5.5μs,所以每个上下文切换大约 4.4μs)。
关于c - 如何更精确地衡量上下文切换的成本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69118118/