测量linux内核中函数的执行时间

Posted

技术标签:

【中文标题】测量linux内核中函数的执行时间【英文标题】:Measuring execution time of a function inside linux kernel 【发布时间】:2011-01-11 08:45:08 【问题描述】:

我正在使用 Linux 安全模块挂钩向 recv() 系统调用添加一些自定义功能。与原始的 recv() 相比,我想测量此功能的开销。我编写了一个简单的 tcp 服务器,可以在有和没有模块的情况下运行。此 tcp 服务器调用 recv() 函数“N”次。它测量每个 recv 所花费的时间,例如:

clock_gettime(before);
recv()
clock_gettime(after);
global_time += after - before.

最后,我用“global_time/N”打印单个recv() 的平均时间。让我们将此时间称为“user_space_avg_recv”时间。

在我的模块中,我想放置时间测量函数来计算我的钩子的确切执行时间。我尝试了 3 种方法。

    我使用 jiffies 如下:

    sj = jiffies;
    my_hook();
    ej = jiffies;
    current->total_oh = ej - sj;
    

    但我发现 sj 和 ej 值之间没有区别。因此 total_oh 没有改变。

    我使用 current_kernel_time() 因为我认为它以纳秒为单位返回时间。但是,再一次,前后时间没有区别。

    我使用了 get_cycles。我打印进程退出时的总周期。但是,当我将总周期值转换为毫秒时,它会远大于 “user_space_avg_recv”值。这没有意义,因为内核内部的测量值总是小于从用户空间测量的时间值。这可能意味着我要么没有使用正确的 API 进行测量,要么在将值从周期转换为毫秒时出错。

我基本上使用以下公式将周期转换为毫秒:

avg overhead of my hook in milliseconds = 
             (((cycles / 2.99) / 10^6) / N)

2.99 因为我的时钟频率是 2.99Ghz

几点:

我的用户空间程序使用 set affinity 绑定到单个内核。

我使用的是内核 2.6.22.14

为了阻止内核在我的钩子中切换上下文,我使用了 preempt_disable() 和 preempt_enable()。因此它不会计算其他内核线程的执行时间。即使这样,由于我的钩子使用了一些 I/O,我的线程可能会自愿释放控制,或者可能会发生一些中断,这可能会增加总周期数。

问题: 如何在内核中准确测量函数执行时间?

【问题讨论】:

尝试在 Bios 中禁用频率更改并按周期重新测量 你试过ktime_get吗? 【参考方案1】:

您可以使用function tracer API 来跟踪所有函数调用和返回,并带有高精度时间戳。这包括中断事件和上下文切换。然后,您可以分析用户空间中生成的跟踪,以准确了解您的函数需要多长时间运行。

如果您不能使用函数跟踪器 API,您可以调用 do_gettimeofday() 来获取微秒分辨率的时间戳,或调用 getnstimeofday() 来获取纳秒分辨率。这些与用户空间 gettimeofday() 调用在内部使用的功能相同。当然,对于非常快速的函数,这可能不够准确;任何比这更快的精度,您可能需要深入研究计时器代码以查看它如何实现周期转换。另请注意,仅仅因为它们具有高分辨率并不意味着它们具有那么高的准确性 - 但它们应该对基准测试有用。

请注意,任何形式的跟踪都会导致额外的延迟 - do_gettimeofday() 需要许多原子比较和交换操作,并且 ftrace 将日志代码放在每个单个函数的前置和后置码时间>。在解释结果时应考虑到这一点。

【讨论】:

那不是为 2.6.28 以后写的吗?我正在使用 2.6.22.14。 @Methos:用另一种方法更新。 @bdonlan,感谢您的更新。但是,do_gettimeofday() 在内部从“xtime”读取值。我在数字 2 中提到的 current_kernel_time() 函数也有同样的作用(它在同一个文件 kernel/time.c 中)。正如我在第 2 点中所说,我没有看到在我的钩子之前和之后返回的值之间有任何时间差。 实际上,它们是不同的——xtime 会定期更新,因此精度有限。 do_gettimeofday 根据 CPU 周期计数应用偏移量,以获得更高的结果精度。比较我链接到的do_gettimeofday() 的代码和current_kernel_time 的代码(lxr.linux.no/linux+v2.6.22.14/kernel/time.c#L219) 我想你说的是从 do_gettimeofday() 中的 time_interpolator_get_offset() 获得的偏移量。我不知道这是否确实计算了 CPU 周期(但我会相信你的话)。在任何情况下,do_gettimeofday() 在“struct timeval”中返回值,其精度为秒和微秒。我需要 struct timespec,它的精度为 sec 和 nanosec。我想我必须添加我自己的 my_gettimeofday(),它基本上不会将 nsec 转换为 microsec。我将对内核进行更改,编译并测试您的理论。再次感谢。【参考方案2】:

我不确定你会得到你想要的结果,但我们使用以下代码来获得微秒。

double Microsecs()

   static struct timeval _t;  
   static struct timezone tz;  
   gettimeofday(&_t, &tz);  
   return   (double)_t.tv_sec + (double)_t.tv_usec/(1000*1000);

比你在你想要的调用之前和之后调用它,看看它有多少次。 我们一直在使用这种方法来评估 IO 时间监控读/写/寻道操作,以优化性能,并且取得了不错的效果。

HTH。

【讨论】:

为什么是“静态结构”而不是普通结构?对我来说,这似乎是一种竞争条件...... 你可能是对的。在单线程代码中减少了堆栈上的活动量,特别是当您在测试中调用它超过 10^6 次并执行非常多的非常小的时间片总和时。但是,无论如何,你可能是对的。我 9 年前做过,我实际上不记得是否检查过性能差异。【参考方案3】:

您尝试过使用 OProfile 吗?

【讨论】:

我想过。但是我想分别分析每个进程的钩子。我不知道如何使用 Oprofile 来做到这一点,我认为这种方法会更容易。关于如何使用 Oprofile 执行此操作的任何指示? @Dan:这不是答案。您可以通过发表评论来澄清这些事情。

以上是关于测量linux内核中函数的执行时间的主要内容,如果未能解决你的问题,请参考以下文章

如何测量 Linux 和 Windows 中函数的“用户”执行时间

如何在 Linux 多线程环境中测量函数的总执行时间

如何在golang中测量函数的执行时间,不包括等待时间

在 R 中测量函数执行时间

在 R 中测量函数执行时间

在 R 中测量函数执行时间