缓存刷新后的计时非常不确定
Very high uncertainty in timings after cache flush
我使用以下代码来测试初始化后在运行时daxpy例程上刷新缓存的效果(fill()和wall_time()例程的完整代码在这里:http://codepad.org/QuLT3cbD -它是150行):
#define KB 1024
int main()
{
int cache_size = 32*KB;
double alpha = 42.5;
int operand_size = cache_size/(sizeof(double)*2);
double* X = new double[operand_size];
double* Y = new double[operand_size];
//95% confidence interval
double max_risk = 0.05;
//Interval half width
double w;
int n_iterations = 100;
students_t dist(n_iterations-1);
double T = boost::math::quantile(complement(dist,max_risk/2));
accumulator_set<double, stats<tag::mean,tag::variance> > unflushed_acc;
for(int i = 0; i < n_iterations; ++i)
{
fill(X,operand_size);
fill(Y,operand_size);
double seconds = wall_time();
daxpy(alpha,X,Y,operand_size);
seconds = wall_time() - seconds;
unflushed_acc(seconds);
}
w = T*sqrt(variance(unflushed_acc))/sqrt(count(unflushed_acc));
printf("Without flush: time=%g +/- %g nsn",mean(unflushed_acc)*1e9,w*1e9);
//Using clflush instruction
//We need to put the operands back in cache
accumulator_set<double, stats<tag::mean,tag::variance> > clflush_acc;
for(int i = 0; i < n_iterations; ++i)
{
fill(X,operand_size);
fill(Y,operand_size);
flush_array(X,operand_size);
flush_array(Y,operand_size);
double seconds = wall_time();
daxpy(alpha,X,Y,operand_size);
seconds = wall_time() - seconds;
clflush_acc(seconds);
}
w = T*sqrt(variance(clflush_acc))/sqrt(count(clflush_acc));
printf("With clflush: time=%g +/- %g nsn",mean(clflush_acc)*1e9,w*1e9);
return 0;
}
当我运行这段代码时,它报告了其中的比率和不确定性(95%置信水平)的这些数字:
无冲洗:时间=3103.75±0.524506 ns与clflush: time=4651.72 +/- 201.25 ns
为什么用clflush从缓存中刷新操作数X和Y会使测量中的噪声增加100倍以上?
在3GHz下。52 ns是1.5 CPU周期,201.25 ns是604 CPU周期…考虑到需要几百个CPU周期或更多的时间从DRAM读取缓存线,当它在缓存层次结构中丢失时,您正在测量由1或2个缓存线丢失引起的方差……这并不多。在未冲洗的情况下,你有一个非常紧密的平均时间读数与非常紧密的方差。没有缓存丢失。
我发现,通过在我的Mac上将迭代次数增加到5000左右(你有100次迭代),我可以获得与未刷新情况一样严格的刷新情况的方差读数。顺理成章地,同时当缓存中的数据不会比当它都是在缓存中,但它并不像缓慢如您所料,这是因为CPU是非常有效的在你的情况下,访问模式和预测(大胆的)预抓取数据之前,您的预期使用(许多未来的高速缓存线路,事实上,隐藏的相对较长的延迟DRAM)。
CPU可能会在预期使用之前发出几个数据缓存(和指令缓存)预取…通过同时进行许多内存读取,它有效地减少了内存延迟(当然,如果它猜测正确的话)。这就引入了非决定论。在未刷新的情况下,几乎所有数据都在1级数据缓存中——堆栈内存引用是32KB数据的补充,因此会溢出L1数据缓存,但这并不多,很快就会被2级缓存填满——关键是不需要转到内存控制器/DRAM。在刷新的情况下,您的数据仅在内存中,因此您可以根据处理器预取如何争夺内存控制器(数据和指令)以及共享相同内存控制器的其他内核中发生的情况获得可变性。通过长时间运行它,您可以让系统适应该访问模式的"正常"模式,并且差异减小。
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 不确定如何装饰我的C++库代码以在 C 中使用
- C++功能泄漏内存,我是C++新手,不确定如何解决
- "错误 C0000:语法错误,令牌"<EOF>"处出现意外$end,并且不确定
- 代码在 CodeSignal 中工作不正确。不确定这是否是我的代码缺陷
- 为什么以下代码是不确定的?
- 编译错误:"lvalue required as left operand of assignment" 不确定原因
- 误用指针会产生分段错误。我仍然不确定如何纠正它
- 我不确定为什么代码没有运行,它在编译时抛出错误,说它无法找到 PrintArray()
- 不确定出了什么问题(字符串输入)
- C++睡眠时间不确定
- 在C++中做一个场景问题,我不确定如何处理布尔和if循环
- 代码输出随机符号,我不确定出了什么问题
- 可变值变化,不确定原因
- 有没有办法让C++从 cin 中接收不确定数量的字符串?
- 尝试一次将单个字符读取到大小不确定的数组中
- 我不确定如何引用此神经网络训练方法中的权重
- 函数参数:不确定排序还是未排序?
- std::random_device如何生成不确定的随机数?
- 缓存刷新后的计时非常不确定