for (int i = 0; i < 5000; i++)
for (int j = 0; j < 5000; j++)
{
for (int ii = 0; ii < 20; ii++)
for (int jj = 0; jj < 20; jj++)
{
int num = matBigger[i+ii][j+jj];
// Extract range from this.
int low = num & 0xff;
int high = num >> 8;
if (low < matSmaller[ii][jj] && matSmaller[ii][jj] > high)
// match found
}
}
该机器是x86_64,32kb L1 cahce,256 Kb L2缓存。
关于如何优化此代码的任何指示?
编辑原始问题的一些背景知识:Fastest way to Find a m x n submatrix in M X N matrix
答案 0 :(得分:4)
我尝试的第一件事是将ii
和jj
循环移到i
和j
循环之外。这样你就可以matSmaller
使用相同的i
元素进行j
和for (int ii = 0; ii < 20; ii++)
for (int jj = 0; jj < 20; jj++)
int smaller = matSmaller[ii][jj];
for (int i = 0; i < 5000; i++)
for (int j = 0; j < 5000; j++) {
int num = matBigger[i+ii][j+jj];
int low = num & 0xff;
if (low < smaller && smaller > (num >> 8)) {
// match found
}
}
循环的2500万次迭代,这意味着你(或编译器,如果你是幸运的话) )可以在这些循环之外提升对它们的访问:
matSmaller
这可能会更快(感谢对matBigger
数组的访问权限较少),或者速度可能较慢(因为我已经更改了对ii
数组的访问模式,并且&# #39;我可能认为它对缓存不太友好)。类似的替代方法是将i
循环移到j
和matSmaller[ii]
之外并提升jj
,但将jj
循环留在里面。经验法则是,与早期索引相比,它在内循环中递增多维数组的 last 索引的缓存更友好。所以我们更开心了#34;修改j
和ii
,而不是修改i
和matBigger
。
我要尝试的第二件事 - int
的类型是什么?看起来其中的值仅为16位,因此请将其作为(u)int16_t
和int
进行尝试。前者可能更快,因为对齐smaller
访问速度很快。后者可能更快,因为任何时候更多的数组都适合缓存。
对于0
的早期分析,您可以考虑一些更高级别的事情:例如,如果它matBigger
那么您不需要检查ii
对于jj
和num & 0xff < 0
的值,因为{{1}}始终为false。
做得比做出更好的事情,然后看看他们是否更快或更快。&#34;你需要知道哪些线路最热,这意味着你需要一个分析器。
答案 1 :(得分:2)
一些基本建议:
const
,以便向编译器提示更多内容。high
测试失败时不计算low
。matBigger
和matSmaller
,最内层步入简单的增量。答案 2 :(得分:1)
最好的事情是了解代码应该做什么,然后检查是否存在针对此问题的其他算法。
除此之外:
// match found
的位置中突破所有3个循环。operator()(int,int,int)
的数组可以更有效地访问元素。答案 3 :(得分:0)
什么是matSmaller
和matBigger
?
尝试将其更改为matBigger[i+ii * COL_COUNT + j+jj]
答案 4 :(得分:0)
我同意史蒂夫关于重新安排你的循环以获得更高的计数作为内循环。由于您的代码只进行加载和比较,我相信很大一部分时间用于指针算术。尝试一个实验来改变史蒂夫的答案:
for (int ii = 0; ii < 20; ii++)
{
for (int jj = 0; jj < 20; jj++)
{
int smaller = matSmaller[ii][jj];
for (int i = 0; i < 5000; i++)
{
int *pI = &matBigger[i+ii][jj];
for (int j = 0; j < 5000; j++)
{
int num = *pI++;
int low = num & 0xff;
if (low < smaller && smaller > (num >> 8)) {
// match found
} // for j
} // for i
} // for jj
} // for ii
即使在64位模式下,C编译器也不一定能很好地将所有内容保存在寄存器中。通过将数组访问更改为简单的指针增量,您将使编译器的工作更容易生成有效的代码。
编辑:我刚注意到@unwind建议基本相同的事情。另一个需要考虑的问题是您的比较统计数据。低或高比较更可能吗?安排条件陈述,以便首先进行较不可能的测试。
答案 5 :(得分:0)
看起来这里有很多重复。一种优化是减少重复工作量。使用笔和纸,我显示matBigger
“i”索引迭代为:
[0 + 0], [0 + 1], [0 + 2], ..., [0 + 19],
[1 + 0], [1 + 1], ..., [1 + 18], [1 + 19]
[2 + 0], ..., [2 + 17], [2 + 18], [2 + 19]
正如您所看到的,有多次访问的位置。 此外,乘以迭代计数表示访问内部内容:20 * 20 * 5000 * 5000,或10000000000(10E + 9)次。那太多了!
因此,不要试图加速执行10E9指令(例如执行(管道)缓存或数据缓存优化),而是尝试减少迭代次数。
代码在矩阵中搜索一个范围内的数字:大于最小值且小于最大范围值。
基于此,尝试不同的方法:
目标是减少重复访问次数。锚点允许一次扫描并允许其他决定,例如查找范围或确定包含锚值的MxN矩阵。
另一个想法是创建包含matBigger
和matSmaller
的新数据结构,这些数据结构更适合搜索。
例如,为matSmaller
中的每个唯一值创建一个{value,coordinate list}条目:
Value coordinate list
26 -> (2,3), (6,5), ..., (1007, 75)
31 -> (4,7), (2634, 5), ...
现在,您可以使用此数据结构在matSmaller
中查找值并立即了解其位置。因此,您可以在matBigger
中搜索此数据结构中的每个唯一值。这再次减少了对矩阵的访问次数。