getrusage(RUSAGE_THREAD, &r_usage)中的用户CPU时间和系统CPU时间具体是指什么?

What do the user CPU time and system CPU time in getrusage(RUSAGE_THREAD, &r_usage) measure excactly?

所以我想知道当前线程到目前为止执行的时间。我正在尝试使用 getrusage(RUSAGE_THREAD, &r_usage); 。这是我的困惑:

1- 由该函数 return 计算的时间是否包括线程被阻塞(例如,在条件变量上)或调度的时间?

2-线程因其他原因被阻塞的时间如何,例如已屏蔽 I/O?

3- 我可以提高时间精度,使 getrusage(RUSAGE_THREAD, &r_usage); 到 return 纳秒吗?

非常感谢!

似乎很难相信阻塞时间(无论出于何种原因)会计入执行时间——但如果确实如此,让我们做一个快速实验来了解一下:

#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <iostream>
#include <iomanip>

std::ostream &operator<<(std::ostream &os, timeval const &t) {
    return os << t.tv_sec << '.' << std::setw(6) << std::setfill('0') << t.tv_usec << std::setfill(' ');
}

int main() {
    rusage r;

    getrusage(RUSAGE_THREAD, &r);
    std::cout << r.ru_utime << " [" << r.ru_stime << "]\n";
    sleep(10);
    getrusage(RUSAGE_THREAD, &r);
    std::cout << r.ru_utime << " [" << r.ru_stime << "]\n";
}

结果:

0.000000 [0.000000]
0.000000 [0.000000]

所以,睡觉的时间是不收费的。我们可以用互斥体做类似的测试:

#include <sys/time.h>
#include <sys/resource.h>
#include <iostream>
#include <iomanip>
#include <thread>
#include <mutex>

using namespace std::literals;

std::ostream &operator<<(std::ostream &os, timeval const &t) {
    return os << t.tv_sec << '.' << std::setw(6) << std::setfill('0') << t.tv_usec << std::setfill(' ');
}

int main() {
    rusage r;

    getrusage(RUSAGE_THREAD, &r);
    std::cout << r.ru_utime << " [" << r.ru_stime << "]\n" << std::flush;

    std::mutex m;
    m.try_lock();   // ensure mutex is locked

    // create thread to unlock mutex after 5 seconds:
    auto t = std::thread([&]{ std::this_thread::sleep_for(5s); m.unlock(); });

    // wait for mutex to be unlocked
    m.lock();

    // re-check resource usage
    getrusage(RUSAGE_THREAD, &r);
    std::cout << r.ru_utime << " [" << r.ru_stime << "]\n";
    t.join();
}

这给出了相似的结果。我没有费心为条件变量重复它​​,但我很惊讶地看到它的不同结果(condvar 总是与互斥体相关联,它是你真正等待的互斥体)。

尝试测试每一种可能的 I/O 不太实际,但过去的经验表明它也是如此:阻塞等待 I/O 的时间不被视为执行时间。

就精度而言,我想你 可以 理论上将精度提高到纳秒级,但这需要重写内核,而不仅仅是拨动开关或调用不同的功能。实际上,即使那样,您是否能够获得 much 更准确,还有很多问题。如果您需要纳秒级精度,这可能不是适合这项工作的工具。

不,一般来说阻塞时间不会分配给用户或内核CPU时间(这是usage衡量的)。 rusage 计时器基本上是一个 "wall clock timer",由 OS 启动和停止:当进程被调度时,它会记录时间,当它被取消调度时,它会停止它(类似于user/kernel 在 entry/exit 上拆分为内核例程)。所有这些段的总和是 CPU 时间。

在某些情况下,例如 IO,内核可能正在做真正的工作,而不是等待,并且可能分配给您的进程。

如果您想要 CPU 时间 更高的价值,您应该查看性能计数器。在 Linux 上,您可以使用 perf_events 系统以虚拟化方式访问这些计数器,或者使用像 PAPI 这样的库来包装对该子系统的访问,或者最容易上手的是使用像 libpfc 这样的轻量级的东西,它提供了直接的计数器访问。

你还问:

Moreover, how can I include the time spent on I/O?

这是一个棘手的问题,因为现代系统上 IO 的异步和缓存特性。很难准确确定花费的时间以及如何分配它(例如,如果一个进程将一页从磁盘带入缓存,但随后有 10 个其他进程访问它,您如何分配 IO 时间)?一种想法是查看 /proc/pid/ 计数器和可能具有 blocking-on-IO 指示符的状态条目。事实上,top 可以显示处于这种状态的进程,因此您可能会从 /proc/$pid 中获取它。请注意,我认为您必须从其他线程对该文件系统进行采样才能使其正常工作。

或者,您可以尝试在应用程序级别检测您的 IO 调用。最后,您可以使用 ptrace 或 Linux 上更新的 FTrace 之类的东西在内核级别检测 IO 调用,并且您可以按进程过滤它。较新的内核显然有 "per-process IO accounting" - 但我无法快速挖掘出一个好的 link。 iotop 的来源可能有您需要的电话。

最后,这完全取决于您所说的当前线程到目前为止已经执行的时间的意思:如果您想包括 IO,也许您只需要 wall-clock线程启动后的时间?然后你可以使用 clock_gettime 和朋友,它们提供纳秒分辨率(名义上,但调用本身至少需要十几纳秒,所以你不会准确地测量需要 1 或 2 纳秒的东西)。