8

知道循环将经历的迭代次数允许编译器进行一些优化。例如考虑下面的两个循环:

未知的迭代次数:

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 在优化时考虑的循环计数进行断言?

4

2 回答 2

6

函数上有hot属性可以向编译器提示有关热点: http: //gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html。只是在你的函数之前 abb:

static void bitreverse(vbuf_desc * vbuf) __attribute__ ((pure));

hot这里是来自 gcc的关于 ' ' 的文档:

hot函数的hot属性用于通知编译器该函数是编译程序的热点。该功能进行了更积极的优化,并且在许多目标上它被放置在文本部分的特殊小节中,因此所有热门功能看起来都非常接近,从而提高了局部性。当配置文件反馈可用时,通过 -fprofile-use 会自动检测热功能并忽略此属性。

函数的 hot 属性在 4.3 之前的 GCC 版本中没有实现。

标签上的 hot 属性用于通知编译器标签后面的路径比没有注释的路径更有可能。此属性用于无法使用 __builtin_expect 的情况,例如使用计算的 goto 或 asm goto。

标签上的 hot 属性在 4.8 之前的 GCC 版本中没有实现。

您也可以尝试在您的周围添加 __builtin_expect idx < vbuf->bytesused- 这将暗示在大多数情况下该表达式是正确的。

在这两种情况下,我都不确定您的循环是否会得到优化。

或者,您可以尝试配置文件引导的优化。-fprofile-generate使用;构建程序的配置文件生成版本 在目标上运行它,将配置文件数据复制到构建主机并使用-fprofile-use. 这将为编译器提供大量信息。

在某些编译器中(不在 GCC 中)有循环编译指示,包括 " #pragma loop count (N)" 和 " #pragma unroll (M)",例如在Intel中;在 IBM 展开在 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?主题=/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]++;
}
于 2013-01-10T12:20:24.010 回答
0

您实际上可以使用 __builtin_expect 指定确切的计数,如下所示:

while (idx < __builtin_expect(vbuf->bytesused, 1280*400)) {

这告诉 gccvbuf->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]);
}

这是编译器无法执行的优化,无论您提供什么信息。

于 2013-01-10T12:56:28.393 回答