【问题标题】:Why my time count of a system call in linux is almost 0 us?为什么我在 linux 中系统调用的时间计数几乎是 0 us?
【发布时间】:2019-10-14 23:55:58
【问题描述】:

现在,我正在学习系统内核实践课程。但是,当我将系统调用与用户调用进行比较时,奇怪的是系统调用返回的时间计数为 0 us(有时返回 1)。但是我通过了 count=1e8,这是一个很大的数字。

我怀疑计算没有发生,因为没有使用结果。然后我改变 add as result = result + 1 并打印最终结果。但是,结果是对的,时间计数只是从 0 或 1 变为 2-6。

long yanpan_oper(int* result,int num1,int num2,char* op)
{
    if(op)
    {
        if(*op == '+')
        {
            *result = num1 + num2;
        }
        else if(*op == '-')
        {
            *result = num1 - num2;
        }
        else if(*op == '*')
        {
            *result = num1*num2;
        }
        else if(*op == '\\')
        {
            if(num2!=0)
                *result = num1/num2;
            else
                printk("divided number can't be zero!\n");
        }else
            printk("unrecongized operator %c\n", *op);
    }else
    {
        printk("operation is empty.\n");
    }
    return 0;
}
SYSCALL_DEFINE1(yanpan_func, int, count)
{
    printk("The count is %d.\n", count);
    struct timeval tstart, tend;
    do_gettimeofday(&tstart);
    int i;
    for(i=0;i<count;i++) // +
    {
        int result;
        char op_add = '+';
        yanpan_oper(&result, i, 10, &op_add);
    }
    for(i=0;i<count;i++) // -
    {
        int result;
        char op_sub = '-';
        yanpan_oper(&result, i, 10, &op_sub);
    }
    for(i=0;i<count;i++) // *
    {
        int result;
        char op_mul = '*';
        yanpan_oper(&result, i, 2, &op_mul);
    }
    for(i=0;i<count;i++) // '//'
    {
        int result;
        char op_div = '\\';
        yanpan_oper(&result, i, 10, &op_div);
    }
    do_gettimeofday(&tend);
    long delta_time = 1000000*(tend.tv_sec - tstart.tv_sec) + (tend.tv_usec - tstart.tv_usec);
    printk("The start time is %ld.\n", tstart.tv_sec*1000000+tstart.tv_usec);
    printk("The end time is %ld.\n", tend.tv_sec*1000000+tend.tv_usec);
    printk("Syscall time use:%ld usec", delta_time);
    return delta_time;
}

我尝试了很多次,但结果没有改变。相同数量的用户调用计算大约需要1300毫秒,内核中的计算可以这样快吗?

【问题讨论】:

  • 我缺少main 函数。请生成minimal reproducible example
  • 另外,在使其最小化的过程中,您可能可以从SYSCALL_DEFINE1 中删除三个循环,从yanpan_oper 中删除三个else if

标签: c linux-kernel operating-system system-calls


【解决方案1】:

我们来看一个循环:

for(i=0;i<count;i++) // +
{
    int result;
    char op_add = '+';
    yanpan_oper(&result, i, 10, &op_add);
}

这将调用函数 yanpan_oper count 次。但每次它都会覆盖存储在result 中的先前结果,而不使用该值进行计算。很有可能编译器只是优化了整个循环,只用一次调用 yanpan_oper 来替换它,因为 for 循环实际上相当于只执行一次循环体。

此外,循环体只影响循环体内的变量,因此编译器不仅可以决定只保留最后一次迭代。它基本上可以跳过整个代码,所以你实际执行的是这样的:

SYSCALL_DEFINE1(yanpan_func, int, count)
{
    printk("The count is %d.\n", count);
    struct timeval tstart, tend;
    do_gettimeofday(&tstart);
    do_gettimeofday(&tend);
    long delta_time = 1000000*(tend.tv_sec - tstart.tv_sec) + (tend.tv_usec - tstart.tv_usec);
    printk("The start time is %ld.\n", tstart.tv_sec*1000000+tstart.tv_usec);
    printk("The end time is %ld.\n", tend.tv_sec*1000000+tend.tv_usec);
    printk("Syscall time use:%ld usec", delta_time);
    return delta_time;
}

以下是一些关于如何欺骗优化器的提示:

// Create input that cannot be calculated at compile time
int input1[count];
int input2[count];
srand(time(NULL));
for(int i=0; i<count; i++) { 
    input1[i] = rand()%1000;
    input2[i] = rand()%1000;
}

// Store the output, so that the optimizer cannot take away the loop
int output[count];

// Start timer
for(i=0;i<count;i++) // +
{
    char op_add = '+';
    yanpan_oper(&output[i], input1[i], input2[i], &op_add);
}
// End timer

// Use the output to that the optimizer cannot remove the array, and thus
// also the loop
for(int i=0; i<count; i++) 
    printf("%d ", output[i]);

请注意,这些数组对于堆栈来说可能太大了。如果是这种情况,请改用它:

int *input1 = malloc(count * sizeof(*input1));
int *input2 = malloc(count * sizeof(*input2));
srand(time(NULL));
for(int i=0; i<count; i++) { 
    input1[i] = rand()%1000;
    input2[i] = rand()%1000;
}

int *output = malloc(count * sizeof(*output));

(记得检查malloc是否成功,然后释放内存)

【讨论】:

  • 您好,感谢您的回答。其实我已经猜到原因是编译器的优化了。正如我所提到的,我只是在每次迭代时更改添加代码,它会添加:yanpan_oper(&amp;result, result, 1, op_add)。最后,我打印所有添加后的结果。但时间几乎没有变化。我认为编译器不可能这么聪明。这就是为什么我对此感到奇怪。
  • 有了那个代码,你就有了UB,因为你还没有初始化result,即使你初始化了它,理论上仍然可以在编译时计算。
  • 只有在能够证明函数没有副作用的情况下,才允许编译器“优化”对函数的调用。虽然输入参数的某些值yanpan_oper 实际上没有副作用(不确定输出参数result),但我怀疑编译器能够证明这一点。如果没有这样的证明,编译器就不能减少循环:生成的二进制代码调用yanpan_oper 的次数与源代码中指定的一样多。
  • @Tsyvarev 是的,但为什么不确保优化器不能这样做,而不是疯狂猜测它有多聪明?
  • @Broman:随机值和特别是它们的数组看起来像是一种防止编译器优化代码的丑陋方式。当然,这只是我的感受。但无论如何,检查从原始来源生成的二进制代码是否确实丢弃了函数调用可能很有用。如果不是,那么零时间的问题就在别的地方了……
猜你喜欢
  • 2017-06-03
  • 2012-11-07
  • 2016-06-08
  • 2012-05-04
  • 1970-01-01
  • 2011-04-13
  • 2022-10-18
  • 2010-10-20
  • 1970-01-01
相关资源
最近更新 更多