解释两种几乎相同的算法的性能差异
Explaining performance difference in two nearly identical algorithms
这个问题很模糊,我真的不需要答案,但我很好奇答案是什么,所以我还是会问的。
我有一个生成大量矩阵的算法。它随后在上面运行第二个算法,生成一个解决方案。我跑了100次,平均耗时约17秒。
第二种算法几乎完全相同,唯一的区别是,第二种方法在每个矩阵生成后立即运行,因此它们实际上不需要存储在任何地方。这个变体显然需要更少的空间,这就是我制作它的原因,但对于同样的问题,它平均只需要大约2秒。
我没想到它会跑得更快,尤其是没那么快。
代码相当大,所以我将尝试概述类似伪代码的区别:
recursiveFill(vector<Matrix> &cache, Matrix permutation) {
while(!stopCondition) {
// generate next matrix from current permutation
if(success)
cache.push_back(permutation);
else
recursiveFill(cache, permutation);
// some more code
}
}
recursiveCheck(Matrix permutation) {
while(!stopCondition) {
// alter the matrix some
if(success)
checkAlgorithm(permutation);
else
recursiveCheck(permutation);
// some more code
}
}
在递归填充之后,一个循环在缓存中的所有元素上运行checkAlgorithm。我没有包含在代码中的所有内容在两种算法中都是相同的。我猜向量中的存储一直在消耗,但如果我没有记错的话,每次过满时,c++向量的大小都会翻倍,所以重新分配不应该经常发生。有什么想法吗?
我想额外的时间是由于vector
中矩阵的复制。根据您给出的时间,一次通过数据需要20或170毫秒,这对于大量复制来说是正确的数量级。
请记住,即使由于向量的重新分配而导致的复制开销是线性的,但每个插入的矩阵平均会复制两次,一次在插入期间,另一次在重新分配期间。再加上复制大量数据的缓存破坏效应,这可能会产生额外的运行时。
现在你可能会说:但当我把矩阵传递给递归调用时,我也在复制矩阵,难道我不应该期望第一个算法的时间最多是第二个算法的三倍吗
答案是,如果不受堆上数据缓存利用率的阻碍,那么任何递归的体面都是完全友好的缓存。因此,在递归体面中完成的几乎所有复制甚至都没有到达L2高速缓存。如果您通过vector
重新分配不时地清空整个缓存,那么之后将使用完全冷的缓存继续。
这里的罪魁祸首可能是时间局部性。您的CPU缓存只有这么大,所以当您在每次运行后保存所有内容并稍后返回时,它会在这段时间内离开CPU缓存,并且需要更长的时间(10到100秒的周期)才能访问。对于第二种方法,它就在L1(或者可能是MMX寄存器)中,只需要一两个周期就可以访问。
在优化中,你通常想像吴唐家族一样思考:缓存规则我周围的一切。
有些人对此进行了测试,缓存中的副本通常比主内存中的取消引用便宜得多。
严格来说,vector
不必每次增长两倍,它只需要几何增长即可提供所需的摊余恒定时间。
在这种情况下,如果您有足够多的矩阵,那么增长和所需的数据拷贝仍然可能是问题所在。或者可以通过交换来分配足够的内存。唯一可以确定的方法是在您遇到这种差异的系统上配置。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 为什么这个运算符<重载函数对 STL 算法不可见?
- OpenMP阵列性能较差
- 递归列出所有目录中的C++与Python与Ruby的性能
- 基于ELO的团队匹配算法
- C++选择排序算法中的逻辑错误
- 有没有办法将谓词中的元素偏移量传递给 std 算法?
- C++A*算法并不总是在路径中具有目标节点
- 排序算法c++
- 了解算法的性能差异(如果以不同的编程语言实现)
- 为什么"quick sorting"算法的这两种变体在性能上差异如此之大?
- Dijkstra 最短路径算法性能 std::p riority_queue VS std::set.
- 提高DPLL算法的性能
- 为什么对于许多 SIMD 算法,只有 AVX 的处理器的性能优于 AVX2 处理器
- 为什么这种令人尴尬的并行算法的性能没有随着多线程而提高
- 哪种算法的性能最好
- 排序数组(编辑)时的奇怪算法性能
- 在虚拟机上运行openMp算法造成的性能损失
- Dijkstra算法实现的性能
- 解释两种几乎相同的算法的性能差异