2

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

1- 这个函数返回的时间是否包括线程被阻塞(例如在条件变量上)或被调度的时间?

2- 线程因为其他原因被阻塞所花费的时间,例如 I/O 阻塞?

3-我可以提高以getrusage(RUSAGE_THREAD, &r_usage);纳秒为单位返回的时间精度吗?

非常感谢!

4

2 回答 2

3

似乎很难相信花费在阻塞上的时间(无论出于何种原因)会被计为花费在执行上的时间——但如果是这样,让我们​​做一个快速的实验来获得一个想法:

#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 的时间阻塞不被视为执行时间。

就精度而言,我想理论上你可以将精度提高到纳秒范围,但这将涉及重写内核,而不仅仅是翻转开关或调用不同的函数。实际上,即使在那时,您是否能够获得更准确的结果还有很多疑问。如果您需要纳秒级精度,这可能不是适合这项工作的工具。

于 2017-02-17T17:17:51.490 回答
2

不,通常阻塞时间不会分配给用户或内核 CPU 时间(这是 rusage 衡量的)。计时器基本上是一个由操作系统启动和停止的rusage“挂钟计时器”:当进程被调度时,它会记录时间,当它被取消调度时它会停止它(对于用户/内核在进入/退出时的拆分类似内核例程)。所有这些段的总和就是 CPU 时间。

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

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

你还问:

此外,我如何包括花费在 I/O 上的时间?

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

或者,您可以尝试在应用程序级别检测您的 IO 调用。最后,您可以在 Linux 上使用类似ptrace或更新的东西FTrace在内核级别检测 IO 调用,并且您可以按进程过滤它。较新的内核显然具有“每进程 IO 记帐”——但我无法快速找到一个好的链接。可能的来源iotop会有你需要的电话。

最后,这一切都取决于您到目前为止当前线程执行的时间是什么意思:如果您想包含 IO,也许您只想要线程启动以来的挂钟时间?然后你可以使用clock_gettime和朋友,它提供纳秒分辨率(名义上,但调用本身至少需要十几纳秒,所以你不会准确测量需要 1 或 2 纳秒的东西)。

于 2017-02-17T17:40:21.913 回答