c - 循环交换与循环平铺

标签 c performance loops caching optimization

这些优化中哪一个更好,在什么情况下更好?为什么?

凭直觉,我感觉循环平铺通常会 是更好的优化。

下面的例子呢? 假设一个缓存在任何时候都只能在其中存储大约 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];
        }
    }
}

最佳答案

您在问题中公开的前两个案例大致相同。在以下两种情况下,事情会真正发生变化:

案例 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。将存储在缓存中的第一个“包”元素是 a[0]*a[0] 到 a[4]*a[0]。您的第一个访问不会导致缓存未命中,因此 [0][0] 存储在缓存中,但第二个是!一个0没有存储在缓存中!然后操作系统将缓存元素包 a 04 .然后你执行第三次访问: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;

希望对您有所帮助! (对不起我的英语,我不是母语人士)

关于c - 循环交换与循环平铺,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19033914/

相关文章:

C - 将十六进制转换为字符串

java - 第一次项目构建后,Eclipse 编辑器在 Ubuntu 16.04 上运行缓慢

c# - asp.net mvc razor foreach循环将id添加到div

c++ - 解开 Knuth 的结 : how to restructure spaghetti code?

C *[] 和 ** 之间的区别

c - 如何摆脱预期的 'int(*)(const struct dirent *)' 但参数类型为 'int (*)(struct dirent *)' 错误?

performance - Vim 处理远程文件时性能缓慢

java - 自定义二分查找功能无法正常工作

c - libcurl smtp 消息发送

jquery - 链接 jQuery 选择器有任何负面影响吗?