【问题标题】:Using std::chrono::steady_clock to benchmark code in a thread/async使用 std::chrono::steady_clock 在线程/异步中对代码进行基准测试
【发布时间】:2018-08-26 14:53:33
【问题描述】:

假设我有很多计算要在多个线程中运行(以及基准 CPU 时间)。作为一个玩具示例:

#include <chrono>
#include <future>
#include <iostream>
#include <vector>


using unit_t = std::chrono::nanoseconds;

unit_t::rep expensive_computation() {
    auto start = std::chrono::steady_clock::now();
    // Something time-consuming here...
    auto end = std::chrono::steady_clock::now();

    auto duration = std::chrono::duration_cast<unit_t>(end - start).count();

    return duration;
}

int main() {
    std::vector<std::future<unit_t::rep>> computations;

    for (int i = 0; i < 100; i++) {
        computations.push_back(std::async(expensive_computation));
    }

    for (size_t i = 0; i < computations.size(); i++) {
        auto duration = computations[i].get();
        std::cout << "#" << i << " took " << duration << "ns" << std::endl;
    }
}

我担心因为steady_clock is montonic across threads 基础时钟按进程而不是每个线程滴答(如果安排了任何线程,则时钟滴答为所有 线程)。这意味着如果一个线程正在休眠,steady_clock 仍会为它打勾,而这一次将错误地包含在该线程的duration 中。我的怀疑正确吗?还是steady_clock 仅针对线程内的线程 CPU 时间打勾?

换一种说法,这种方法是否是一种独立计时大量计算的安全方法(这样在一个线程上花费的 CPU 时间不会影响另一个线程的duration)?或者我是否需要为每个计算分离单独的进程以使 steady_clock 仅在计算运行/计划时打勾?

编辑:我也认识到,与内核相比,旋转更多的线程可能是解决此问题的低效方法(尽管我并不特别关心计算吞吐量;此外,我只想要它们作为一个小组以最快的时间完成)。我怀疑在实践中,我需要维护一个小常数的有界线程列表(比如限制在核心数量),并且只有在核心可用时才开始新的计算。但是,这不应该对我上面关心的时间产生影响;它应该只影响挂钟时间。

【问题讨论】:

  • 我认为您应该在 100% 空闲的机器上进行测量。在这种情况下,您使用哪个时钟并不重要(如果您在加载的机器上测量事物,即使您测量线程时间,您的基准也可能不准确:超线程、缓存使用等可能会影响结果)。跨度>
  • 未来就绪检查应该在 future.get() 之前改进代码。在 ns 分辨率(也是 CPU 域)下,检查系统的最小分辨率可能是必不可少的。
  • @geza 这是一个很好的观点。我目前正在努力保护这样的环境。即使在 100% 空闲的机器上,我也会担心的一个问题是,某些计算比其他计算花费的时间要长得多(这些计算可能会受到负载的更大影响,可能会人为地增加它们的时间)。但我怀疑这在很大程度上是不可避免的。
  • @seccpur 您所说的“未来就绪检查”是什么意思? future.get() 不打电话给future.wait() 吗?在玩具示例中,我并不特别关心结果的延迟(只是整体运行时间)。此外,ns 分辨率是这是一个玩具示例的产物。我真正的基准分辨率可能是微秒或毫秒。
  • @BaileyParker:“即使在 100% 空闲的机器上,我也会担心一些计算比其他计算花费的时间要长得多”。你这是什么意思? (请注意:记得关闭 CPU 频率缩放)。

标签: c++ multithreading std future chrono


【解决方案1】:

标准规定steady_clock 模拟物理时间(相对于CPU时间)。

来自 [time.clock.steady]:

steady_clock 类的对象表示时钟,其 time_point 的值不会随着物理时间的增加而减少,time_point 的值相对于实时以稳定的速率增加。也就是说,时钟可能无法调整。

话虽如此,实现对物理时间建模的程度是一个 QOI 问题。不过,您的代码对我来说看起来不错。

如果您的实验结果不令人满意,&lt;chrono&gt; 的客户还可以创建自己的自定义时钟,这些时钟将在 &lt;chrono&gt; 库中具有一流的地位。

【讨论】:

  • 糟糕,我好像在这里误解了。我将通过实际计算进行尝试,看看这是否令人满意(尽管确定是否存在干扰可能会很棘手)。在研究过程中,我确实遇到了pthread_getcpuclockid,所以也许定制时钟包装可能更准确?正如@geza 在 cmets 中指出的那样,考虑到缓存、超线程等,我的目标可能无法实现与串行执行相同的时间。感谢您的洞察!
【解决方案2】:

这意味着如果一个线程正在休眠,steady_clock 将 仍在为它打勾,这次将错误地包含在 该线程的持续时间。

这不会是错误,正如标准所规定的那样 std::chrono::steady_clock 类,它测量物理时间,而不是 CPU 时间或任何其他时间。请参阅[time.clock.steady] 下的此处:

steady_­clock 类的对象表示时钟,其值为 time_­point 永远不会随着物理时间的推移而减少,并且time_­point 的值相对于 实时...

也就是说,您的代码看起来不错,因为它将为您提供每个线程运行所测量的时间。你有理由想在这里测量 CPU 时间吗?如果是这样,请在 cmets 中告诉我。

【讨论】:

  • 感谢标准链接。我会读!物理时间可能还可以(在我的具体情况下,计算不进行 I/O)。我的真正目标是产生的时间应该等同于所有计算是否已经连续运行和计时。只要是这样,那就没问题了。
  • @BaileyParker 是的,就是这样。很高兴我能帮上忙。
  • @BaileyParker 从您对另一个答案的评论中看到,我可能没有完全理解您对准确性的要求。您在帖子中的代码数学上不等同于串行运行相同的任务。就测量执行所有任务的总时间而言,只是等效的。
猜你喜欢
  • 2022-10-06
  • 2016-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-15
  • 1970-01-01
  • 2013-03-10
  • 2020-11-25
相关资源
最近更新 更多