【发布时间】:2021-09-11 15:01:49
【问题描述】:
这个问题是基于意见的,所以这是一个编辑来澄清我的意思。
有什么方法可以对更改double 的值是否比从数组中检索double 花费更多或更少的时间做出有根据的猜测?我知道更快的方法可能是情境性的,问题是是否有任何方法可以预测在给定情况下更快的方法是什么。或者,如果有任何“良好做法”,则应遵循这样的编译器可以进行尽可能多的优化。
此问题基于以下知识:访问给定数据所需的时间取决于它位于 L1、L2、L3 (...) 还是 RAM。由于 L1、L2、...中的空间有限,我相信重复修改单个变量比修改许多不同的变量一次要快一些。但是,我不知道差异有多大,或者是否可以预测/操作哪些数据/指令将位于哪个缓存/RAM 中。
下面是最初所说的问题:
操作所花费的时间(据我所知)与存储您正在使用的信息的内存缓存有关。所以我想知道改变双精度 2N 次的值是否比将 N 双精度存储在数组中然后遍历数组更有效。想法是频繁更改的变量将存储在较低级别的缓存中,因此访问它的速度将比存储在数组中的值快一点。数组足够小,整个数组都可以放入 RAM,重点是不要释放内存。
这两种选择的示例代码如下所示。请注意,这里的计算被简化以更好地描述问题的本质。实际上数组是二维的,tmp1 和 tmp2 的计算量稍大,但仍然只是对索引的简单依赖:
#define DIM 1000
double states[DIM];
double time_derivatives[DIM];
double ambient_state = 3.0;
// Initialize states
for (int pos = 0; pos < DIM; pos++) {
states[pos] = pos;
}
// Alternative 1
double tmp1;
double tmp2;
// Ends
tmp1 = 1;
tmp2 = 2;
time_derivatives[0] = (ambient_state - states[0]) * tmp1 + (states[1] - states[0]) * tmp2;
tmp1 = DIM;
tmp2 = DIM + 1;
time_derivatives[DIM - 1] = (ambient_state - states[DIM - 1]) * tmp2 + (states[DIM - 2] - states[DIM - 1]) * tmp1;
// Bulk
for (int pos = 1; pos < DIM - 1; pos++) {
tmp1 = pos + 1;
tmp2 = pos + 2;
time_derivatives[pos] = (states[pos - 1] - states[pos]) * tmp1 + (states[pos + 1] - states[pos]) * tmp2;
}
// Alternative 2
double flows[DIM + 1];
double tmp1; //Some intermediate, neccesary calculation variable
// Flows at ends
tmp1 = 1;
flows[0] = (states[0] - ambient_state) * tmp1;
tmp1 = DIM;
flows[DIM] = (ambient_state - states[DIM - 1]) * tmp1;
// Flows in bulk
for (int pos = 1; pos < DIM; pos++) {
tmp1 = pos + 1;
flows[pos] = (states[pos] - states[pos - 1]) * tmp1;
}
// Compute time derivatives
for (int pos = 0; pos < DIM; pos++) {
time_derivatives[pos] = flows[pos + 1] - flows[pos];
}
在备选方案 1 中,大量计算在最终 for 循环中“重复”,因为一次迭代中的 (states[pos + 1] - states[pos]) * tmp1 将等于下一次迭代中的 - (states[pos - 1] - states[pos]) * tmp2。在备选方案 2 中,计算所有差异并将其存储在数组 flows 中,从而减少了计算的总数。
问题本质上是,与在数组中存储和访问变量的成本相比,计算操作的成本是多少?是否存在限制案例何时一个比另一个更有效?
【问题讨论】:
-
优化编译器可能会相当显着地重新排序代码。如果您想确定,请测量。
-
没有任何保证。分析这两种方法,看看哪种方法更快。
-
与往常一样,唯一确定的方法就是测量。现代硬件很复杂,即使我们认为我们知道发生了什么,也很容易感到惊讶。我的目标是编写干净、易于理解、自我记录的代码。这通常使编译器更容易进行优化并使维护变得更加容易。只有在分析并确定存在问题后,我才会尝试对一段代码进行微优化。
-
实际上,即使单独进行基准测试也可能会产生误导。绝对确定的唯一方法是同时实现它们的实际应用和衡量
-
我唯一要说的是现代英特尔处理器可以检测和预取串行数据(SSE 中的“流”部分),因此顺序访问内存应该是可取的,因为会有更少的停顿。这两个选项似乎都无法以这种方式访问内存。
标签: c++ c performance optimization processing-efficiency