c++ - 为什么添加两个值在运行时会有如此高的可变性?
问题描述
我编写了一个计时函数,它记录函数的运行时间并计算多次运行的平均值和标准差。我惊讶地发现标准偏差非常高,即使是看似简单的任务,例如添加两个双打。我在 python 中分析了数据(见图表)。c++ 输出是19.6171 ns +/- 21.9653ns (82799807 runs)
在编译时使用:
gcc version 8.3.0 (Debian 8.3.0-19)
/usr/bin/c++ -O3 -DNDEBUG -std=gnu++17
整个测试是在我的个人电脑上完成的,这台电脑并没有闲置,而是运行着一个 DE、一个浏览器、我的 IDE 和其他进程。不过,在测试期间有可用的 RAM。我的带 HT 的双核 CPU 的空闲使用率低于 10%。
在这种情况下,是否会出现从平均值 20 ns 到 50 µs 的尖峰?
运行时间图
这是 的内容std::vector<double> run_times
。我没有看到任何模式。
运行时间的直方图
注意 log y 轴(此 bin 中的样本数)。
计时.h
#include <cstdint>
#include <ostream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <chrono>
#include <numeric>
#include <fstream>
struct TimingResults{
// all time results are in nanoseconds
double mean;
double standard_deviation;
uint64_t number_of_runs;
};
std::ostream& operator<<(std::ostream& os, const TimingResults& results);
template <typename InputIterator>
std::pair<typename InputIterator::value_type, typename InputIterator::value_type>
calculate_mean_and_standard_deviation(InputIterator first, InputIterator last){
double mean = std::accumulate(first, last, 0.) / std::distance(first, last);
double sum = 0;
std::for_each(first, last, [&](double x){sum += (x - mean) * (x - mean);});
return {mean, std::sqrt(sum / (std::distance(first, last) - 1))};
}
template<uint64_t RunTimeMilliSeconds = 4000, typename F, typename... Args>
TimingResults measure_runtime(F func, Args&&... args){
std::vector<double> runtimes;
std::chrono::system_clock::time_point b;
auto start_time = std::chrono::high_resolution_clock::now();
do {
auto a = std::chrono::high_resolution_clock::now();
func(std::forward<Args>(args)...);
b = std::chrono::high_resolution_clock::now();
runtimes.push_back(std::chrono::duration_cast<std::chrono::nanoseconds>(b - a).count());
} while (std::chrono::duration_cast<std::chrono::milliseconds>(b-start_time).count() <= RunTimeMilliSeconds);
auto [mean, std_deviation] = calculate_mean_and_standard_deviation(runtimes.begin(), runtimes.end());
return {mean, std_deviation, runtimes.size()};
}
计时.cpp
#include <iostream>
#include "timing.h"
std::ostream& operator<<(std::ostream& os, const TimingResults& results){
return os << results.mean << " ns" << " +/- " << results.standard_deviation << "ns ("
<< results.number_of_runs << " runs)";
}
主文件
#include "src/timing/timing.h"
#include <iostream>
int main(){
auto res = measure_runtime([](double x, double y){return x * y;}, 6.9, 9.6);
std::cout << res;
}
解决方案
现代 CPU 很容易以几个 10^9 FLOPS 的顺序执行,即一次操作的预期时间低于 1 ns。然而,这指的是峰值性能。对于大多数现实世界的工作负载,由于内存和缓存的影响,性能会低得多。
您的基准测试的问题是您正在计时单个操作。a
获取时间点的开销b
很可能只是超过了您实际尝试测量的时间。此外,evenstd::chrono::high_resolution_clock
不会为您提供皮秒精度(尽管这在原则上是实现并且取决于硬件)。显而易见的解决方法是执行操作N
次数,时间为 ,然后将总时间除以N
。在某些时候,您会看到您的结果变得一致。(随时发布您的结果。)
TL;DR:您正试图用怀表计时。
推荐阅读
- python - AttributeError:“str”对象在 MLPRegressor 和 random_state 中没有属性“decode”
- javascript - React/React-spring:如何从 html 流中获取元素/组件?
- joi - joi.label ,这种 joi 模式验证方法有什么作用
- azure - Azure 中的单区域部署和高可用性
- flutter - 如何在颤动中处理动画控制器
- c++ - 使用位集生成二进制数导致c ++中的分段错误
- javascript - 将 Dygraph 数据导出到本地 CSV
- php - 如何在多个用户之间共享对象
- c++ - 从 Cpp 中的多线程 Opencv 捕获视频
- android - Ionic 5 - 无法显示来自 API 的数据