知道循环将经历的迭代次数允许编译器进行一些优化。考虑下面的两个循环:
未知的迭代次数:
static void bitreverse(vbuf_desc * vbuf)
{
unsigned int idx = 0;
unsigned char * img = vbuf->usrptr;
while(idx < vbuf->bytesused) {
img[idx] = bitrev[img[idx]];
idx++;
}
}
已知迭代次数
static void bitreverse(vbuf_desc * vbuf)
{
unsigned int idx = 0;
unsigned char * img = vbuf->usrptr;
while(idx < 1280*400) {
img[idx] = bitrev[img[idx]];
idx++;
}
}
第二个版本将编译为更快的代码,因为它将被展开两次(至少在ARM上使用gcc 4.6.3和-O2)。有没有办法对gcc在优化时考虑的循环计数进行断言?
答案 0 :(得分:6)
函数有hot
属性可以向编译器提供有关热点的提示:http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html。只是在你的职能之前abb:
static void bitreverse(vbuf_desc * vbuf) __attribute__ ((pure));
此处有关于来自gcc的“hot
”的文档:
hot 函数的hot属性用于通知编译器 该函数是编译程序的热点。功能 优化得更加积极,并在许多目标上进行优化 文本部分的特殊部分,所以出现所有热门功能 密切合作改善地方。当个人资料反馈可用时, 通过-fprofile-use,可以自动检测热门功能 属性被忽略。
函数的hot属性未在GCC版本中实现 早于4.3。
标签上的hot属性用于通知编译器该路径 跟随标签的可能性比不是这样的路径更可能 注释。此属性用于__builtin_expect的情况 不能使用,例如使用计算goto或asm goto。
标签上的hot属性未在之前的GCC版本中实现 比4.8。
此外,您可以尝试在idx < vbuf->bytesused
周围添加__builtin_expect - 这将暗示在大多数情况下表达式为真。
在这两种情况下,我都不确定你的循环是否会被优化。
或者,您可以尝试使用配置文件引导优化。使用-fprofile-generate
构建生成配置文件的程序版本;在目标上运行它,将配置文件数据复制到build-host并使用-fprofile-use
重建。这将为编译器提供大量信息。
在某些编译器中(不在GCC 中),存在循环编译指示,包括“#pragma loop count (N)
”和“#pragma unroll (M)
”,例如在Intel; unroll in IBM; vectorizing pragmas in MSVC
ARM编译器(armcc
)也有一些循环编译指示:unroll(n)(通过1):
循环展开:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0348b/CJACACFE.html和http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0348b/CJAHJDAB.html
和__promise内在:
使用__promise来改善矢量化
__promise(expr)内在函数是编译器对给定表达式非零的承诺。这使编译器能够通过优化远离代码来改进矢量化,这些代码基于您所做的承诺是多余的。 示例3.21的反汇编输出显示了__promise产生的差异,通过删除标量修正循环将反汇编减少为简单的矢量化循环。
例3.21。使用__promise(expr)来改进矢量化代码
void f(int *x, int n)
{
int i;
__promise((n > 0) && ((n&7)==0));
for (i=0; i<n;i++) x[i]++;
}
答案 1 :(得分:0)
您实际上可以使用__builtin_expect指定确切的计数,如下所示:
while (idx < __builtin_expect(vbuf->bytesused, 1280*400)) {
这告诉gcc vbuf->bytesused
在运行时预计为1280 * 400。
唉,这对于使用当前的gcc版本进行优化没有任何帮助。但是没有试过4.8。
编辑:刚刚意识到每个标准C编译器都有一种通过断言准确指定循环计数的方法。自断言
#include <assert.h>
...
assert(loop_count == 4096);
for (i = 0; i < loop_count; i++) ...
如果条件不为真,将调用exit()或abort(),任何具有值传播的编译器都将知道loop_count的确切值。我一直认为这将是提供这种优化提示的最优雅和符合标准的方式。现在,我想要一个实际使用此信息的C编译器。
请注意,如果要更快地进行此操作,则按字节展开可能不如使用更宽的查找表有效。 16位表占用128K,因此通常适合CPU高速缓存。如果数据不是完全随机的,则更宽的表(3个字节)可能有效。
2字节示例:
unsigned short *bitrev2;
...
for (idx = 0; idx < vbuf->bytesused; idx += 2) {
*(unsigned short *)(&img[idx]) = bitrev2[*(unsigned short *)(&img[idx]);
}
这是编译器无法执行的优化,无论您提供哪些信息。