哪种优化更好,在什么情况下?为什么呢?
直观地说,我感觉循环平铺通常会发生 是一个更好的优化。
以下示例怎么样? 假设一个缓存,任何时候都只能存储大约20个元素。
Original Loop:
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 1000; j++)
{
a[i] += a[i]*b[j];
}
}
Loop Interchange:
for(int i = 0; i < 1000; i++)
{
for(int j = 0; j < 10; j++)
{
a[j] += a[j]*b[i];
}
}
Loop Tiling:
for(int k = 0; k < 1000; k += 20)
{
for(int i = 0; i < 10; i++)
{
for(int j = k; j < min(1000, k+20); j++)
{
a[i] += a[i]*b[j];
}
}
}
答案 0 :(得分:2)
您在问题中暴露的前两个案例大致相同。在以下两种情况下,情况确实会发生变化:
案例1:
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 1000; j++)
{
b[i] += a[i]*a[j];
}
}
这里你正在访问矩阵“a”,如下所示:a [0] * a [0],a [0] * a 1,a [0] * a [2],...在大多数体系结构中,矩阵结构存储在内存中,如:a [0] * a [0],a 1 * a [0],a [2] * a [0](第一行的第一列)然后是第一个原始的第二列,....)。想象一下,你的缓存只能存储5个元素,你的矩阵是6x6。将存储在高速缓存中的元素的第一个“包”将是[0] * a [0]到[4] * a [0]。您的第一次访问将导致没有缓存未命中,因此[0] [0]存储在缓存中,但第二个是!! 0未存储在缓存中!然后操作系统会将0元素包缓存到4。然后你进行第三次访问:a [0] * a [2]再次没有缓存。另一个缓存未命中!
正如您可以判断的那样,案例1不是解决问题的好方法。它导致大量缓存未命中,我们可以避免更改以下代码:
案例2:
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 1000; j++)
{
b[i] += a[i]*a[j];
}
}
在这里,正如您所看到的,我们正在访问矩阵,因为它存储在内存中。因此,它比案例1好得多(更快)。
关于你发布的关于循环平铺,循环平铺和循环展开的第三个代码是在大多数情况下编译器自动完成的优化。 Here's a very interesting post in stackoverflow explaining these two techniques;
希望它有所帮助! (抱歉我的英语,我不是母语人士)