c - 如何更精确地衡量上下文切换的成本

标签 c operating-system context-switch

我正在编写一些代码来粗略地测量上下文切换的成本。基本思想在教科书里面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);
}

但我对这个问题仍然有一些疑问。

  1. 我看过别人的代码,他们只是使用read(pipefd_2[0], NULL, 0)和write(pipefd_1[1], NULL, 0)来执行读写操作。我不确定如果你在一个进程中还没有向pipe1写入一些数据,而你想在其他进程中通过pipe1读取数据,这种情况会发生上下文切换吗?或者 read 函数只返回 0?

  2. 由于通过管道读取会发生上下文切换,因此上下文切换的精确成本应该是从离开该进程到进入另一个进程,而不包括在后面的进程中执行某些指令的时间,所以我认为用这种方式来计算上下文切换的成本可能不够精确。这是因为与切换上下文相比,执行时间可以忽略不计吗?

感谢您的帮助!

最佳答案

  • #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/

    相关文章:

    ios - 我如何向量化这个 for 循环?

    amqp 上的 C 二进制消息

    python - 为什么在我给出位置参数时 lambda 返回 "<lambda>() takes 0 positional arguments but 1 was given"?

    multithreading - 上下文切换中保存了什么?

    java - 影响多线程操作系统的核心数量

    operating-system - 从哪里从中断返回

    c - ANSI C 中的接口(interface)/实现

    c - 使用 readdir_r 读取目录中的文件并使用 qsort 排序

    我可以在多线程(pthreads)应用程序中捕获 SIGSEGV 和其他信号并打印导致它的线程或所有线程的回溯吗?

    python - 监控同步: Implementing multiple condition variables