这个问题相当含糊,而且我并非真正需要回答它,但我对答案可能是什么非常好奇,所以无论如何我都会问它
我有一个生成大量矩阵的算法。它后来运行第二个算法,生成一个解决方案。我跑了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 ++向量的大小都会翻倍,所以重新分配不应该经常发生。 有什么想法吗?
答案 0 :(得分:2)
这里的罪魁祸首可能是时间地点。你的CPU缓存只是如此之大,所以当你在每次运行后保存所有内容并稍后再回来时,它会同时离开你的CPU缓存并需要更长的时间(10到100秒的周期)才能访问。使用第二种方法,它就在L1(或可能是MMX寄存器)中,并且只需要一个或两个周期来访问。
在优化中,你通常想要像吴唐氏族一样思考:缓存规则我周围的一切。
Some people have done testing on this,缓存中的副本通常 比取消引用主内存便宜。
答案 1 :(得分:2)
我猜这个额外的时间是由vector
内的矩阵复制造成的。根据您提供的时间,一次传递数据需要20或170毫秒,这对于大量复制来说是正确的数量级。
请记住,即使由于向量的重新分配而导致的复制开销是线性的,每个插入的矩阵平均复制两次,在插入期间复制一次,在重新分配期间复制一次。结合复制大量数据的缓存破坏效果,这可以产生额外的运行时。
现在您可能会说:但是当我将它们传递给递归调用时我也会复制矩阵,我不应该期望第一个算法最多花费三倍于第二个算法的时间吗?
答案是,如果不受堆上数据的缓存利用率的影响,任何递归都可以完全缓存友好。因此,几乎所有在递归中完成的复制甚至都没有到达L2缓存。如果您通过vector
重新分配不时地破坏整个缓存,那么之后将恢复完全冷缓存。
答案 2 :(得分:0)
严格来说,vector
并不需要将每次增长加倍,只需几何增长就可以提供所需的摊销常数时间。
在这种情况下,如果您有足够多的矩阵,增长和所需的数据副本仍可能成为问题。或者它可以交换分配足够的内存。确切知道的唯一方法是在您遇到这种差异的系统上 profile 。