如何帮助编译器消除循环和数组?

Posted

技术标签:

【中文标题】如何帮助编译器消除循环和数组?【英文标题】:How to help the compiler eliminate loops and arrays? 【发布时间】:2010-07-14 10:20:09 【问题描述】:

假设您正在编写一个支持多纹理的 3d 渲染器,其中纹理单元的数量通过编译时间常数进行配置。与硬编码单个纹理单元相反,您的代码现在必须通过数组访问纹理相关参数,并通过循环处理它们。

假设一个现代 C++ 编译器,当纹理单元的数量设置为 1 时,为了让编译器生成与硬编码的单个纹理单元等效的代码,您是否需要遵循任何做法?

【问题讨论】:

是的,有。你给我们一些代码,然后我们也可以给你看一些.. 【参考方案1】:

循环和数组有什么问题?

展开循环确实有一个缺点:它会使代码变得更大。更大的代码意味着更多的内存访问来获取代码,并且由于内存访问速度很慢,您的代码最终可能会变慢。此外,英特尔 cpu 对获取的代码进行预处理,并将它们转换为 uOps(微操作),然后调度和执行。 CPU 拥有这些 uOps 的缓存,因此它只解码尚未在缓存中的指令。因此,展开的循环将填满缓存并导致其他代码被撞出。较小的代码通常更好。

至于数组,我不知道你会如何摆脱它们。

所以,如果你有:

struct TextureUnit

  // some texture unit data


TextureUnit units [number_of_units];

for (int i = 0 ; i < number_of_units ; ++i)

  callfunction (units [i].someparams);

这样做可能会更好:

for (TextureUnit *i = unit ; i < &unit [number_of_units] ; ++i)

  callfunction (i->someparams);

但您需要查看编译器在优化构建中生成了什么,以确保它确实提供了任何优势。

我认为这可能被归类为“微优化”,所以我不会真的担心它,除非你能证明它确实是一个瓶颈。记住 - 分析代码,不要只是猜测。

【讨论】:

【参考方案2】:

我对计算机图形学了解不多,但这个简单的测试表明,对于 TEXTURE_COUNT=1 和 -O1,g++ 没有分支。我怀疑这甚至会延伸到许多现实生活中的程序,但你为什么不自己尝试一下。使用-S 查看生成的程序集。

#include <stdio.h>

typedef struct fake_texture

    int r, g, b;
 texture;

int main()

    texture array[TEXTURE_COUNT] = ;
    for(int i = 0; i < TEXTURE_COUNT; i++)
    
    array[i].r += 1;
    array[i].g += 2;
    array[i].b += 3;
    

    for(int i = 0; i < TEXTURE_COUNT; i++)
    
    printf("%d\n", array[i].r);
    

x86 汇编摘录:

main:
.LFB31:
        .cfi_startproc
        .cfi_personality 0x0,__gxx_personality_v0
        pushl   %ebp
        .cfi_def_cfa_offset 8
        movl    %esp, %ebp
        .cfi_offset 5, -8
        .cfi_def_cfa_register 5
        andl    $-16, %esp
        subl    $32, %esp
        movl    $1, 20(%esp)
        movl    $2, 24(%esp)
        movl    $3, 28(%esp)
        movl    $1, 8(%esp)
        movl    $.LC0, 4(%esp)
        movl    $1, (%esp)
        call    __printf_chk
        movl    $0, %eax
        leave
        ret
        .cfi_endproc

【讨论】:

以上是关于如何帮助编译器消除循环和数组?的主要内容,如果未能解决你的问题,请参考以下文章

如何中断或消除 inotifywait 循环?

Java技术专题「编译器专题」重塑认识Java编译器的执行过程(消除数组边界检查+公共子表达式)

循环展开与循环平铺

如何告诉编译器可以安全地并行化循环?

如何消除左递归何时需要消除左递归—编译原理

如何在 Java 编译器中禁用死代码消除?