在C中,我有一个任务,我必须用以下方法进行乘法、反转、转置、加法等。huge矩阵分配为二维数组(数组的数组)。
我找到了 gcc 标志-funroll-all-loops
。如果我理解正确,这将自动展开所有循环,而无需程序员做任何努力。
我的问题:
a)gcc 是否包含这种优化以及各种优化标志:-O1
, -O2
etc.?
b)我必须使用任何pragma
s 在我的代码中利用循环展开还是自动识别循环?
c)如果展开可以提高性能,为什么默认情况下不启用此选项?
d)为了以最佳方式编译程序,推荐的 gcc 优化标志是什么? (我必须运行针对单个 CPU 系列优化的程序,这与我编译代码的机器相同,实际上我使用march=native
and -O2
flags)
EDIT
似乎关于展开的使用存在争议,在某些情况下可能会降低性能。在我的情况下,有多种方法可以在 2 个嵌套的 for 循环中执行简单的数学运算,以迭代对大量元素完成的矩阵元素。在这种情况下,展开如何降低或提高性能?
为什么展开循环?
现代处理器流水线指令。他们喜欢知道接下来会发生什么,并根据指令执行顺序的假设进行各种奇特的优化。
但在循环结束时,有两种可能性!要么返回顶部,要么继续。处理器对将要发生的情况做出有根据的猜测。如果做对了,一切都好。如果没有,它必须刷新管道并暂停一段时间,同时准备采用另一个分支。
正如您可以想象的那样,展开循环消除了分支和这些停顿的可能性,特别是在可能性与猜测相反的情况下。
想象一个代码循环执行 3 次,然后继续。如果您假设(处理器可能会这样做)最后您将重复循环。 2/3的时间,你是对的!但有 1/3 的时间你会停滞不前。
另一方面,想象同样的情况,但代码循环 3000 次。在这里,展开的时间增益可能只有 1/3000。
Why not展开循环?
上述处理器的部分功能涉及将内存中可执行文件的指令加载到处理器的板载指令高速缓存(简称为 I 高速缓存)中。它保存了可以快速访问的有限数量的指令,但当需要从内存加载新指令时可能会停止。
让我们回到前面的例子。假设循环内的代码量相当小n
I 高速缓存的字节数。如果我们展开循环,它现在就占用了n * 3
字节。多一点,但它可能适合单个缓存行,这样您的缓存就会以最佳状态工作,并且不需要停止从主内存读取。
然而,3000 循环展开后需要使用一个巨大的n * 3000
I 高速缓存的字节数。这将需要从内存中进行多次读取,并且可能将程序中其他地方的一些其他有用的内容从 I-cache 中推出。
那我该怎么办?
正如您所看到的,展开为较短的循环提供了更多好处,但如果您打算循环多次,最终会降低性能。
通常,智能编译器会对要展开的循环进行适当的猜测,但如果您愿意,您可以强制它sure你比较清楚。如何更好地了解?唯一的方法就是两种方法都尝试一下并比较时间!
过早的优化是万恶之源——唐纳德·高德纳
首先进行配置,然后进行优化。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)