代码之家  ›  专栏  ›  技术社区  ›  izac89

内存分配优化

  •  2
  • izac89  · 技术社区  · 6 年前

    众所周知,编译器可以混合分配顺序以优化执行, 所以-

    a=b;
    c=d;
    

    可以实际执行

    c=d;
    a=b;
    

    但是,使用以下代码:

    a=b;
    x=a;
    func(x);
    

    到…的时候 func(x) 被称为, x 必须包含 b 首先,否则结果可能无法预测。

    现在,下面的代码如何:

    int *addr1 = some_addr;
    int *addr2 = (int *)0xf00;
    
    /* The following applies:
     *      some_other_addr >= some_addr
     */
    for (addr1; addr1 < some_other_addr; addr1++) 
    {
        *addr1 += 1;
    }
    
    *addr2 *= 8;
    

    什么时候 addr2 指向的范围内的地址 for loop,我们需要知道是否承诺 *addr2 将在8中的倍数之前递增,如果不是,则放置一些优化步骤 *addr2 *= 8; 对于 循环,结果 *地址2 将不同于在没有优化的情况下执行。

    在这种情况下,答案会有所不同吗 some_addr some_other_addr 是否在范围中定义,以及是否作为参数传递?因为在第一种情况下,编译器很容易知道 *地址2 对于 循环,而在第二种情况下,它不是那么明显。

    而且,如果我们从组装的角度来看,让我们以一个示例为例 reset_handler 的代码段 bss 节初始化:

        ldr  r1, =__BSS_SIZE__
        cmp  r1, #0
        beq  FINISHED_LABEL
        ldr  r0, =__BSS_START__
        ldr  r2, =0
    LOOP_LABEL:
        str  r2, [r0]
        add  r0, r4
        subs r1, r4
        bne  LOOP_LABEL
    

    如果此代码之后的下一条指令(位于 FINISHED_LABEL )加载值( ldr )从bss范围内的地址,是否承诺内容届时有效(0)?

    1 回复  |  直到 6 年前
        1
  •  4
  •   Peter Cordes    6 年前

    编译器必须做的事情是 "alias analysis"

    如果编译器能证明 addr2 不在范围之内 addr1 循环,它可以重新排序或保留 *addr2 在整个循环的寄存器中。

    对于以下情况,这是一个非常有用的优化 for(...; addr1++) { *addr1 += *addr2; } 避免重新加载 地址2 每次都是因为 the restrict keyword 存在。

    如果输入可能重叠,编译器可以(和 )发出代码 支票 用于重叠 如果没有重叠,则运行优化(例如自动矢量化)循环,如果有重叠,则运行安全循环。


    如果编译器 不能 证明转换将给出与C抽象机相同的最终结果,但它不能进行转换。(我说“final”是因为存储到内存的顺序不是可观察结果的一部分,除非您使用 std::atomic 。所以编译时转换不允许中断 单螺纹 代码,非常类似于无序CPU所做的:提供按程序顺序发生的一切的假象 对于单线程 .)

    “好像”规则只允许在 全部的 不导致UB的案例 ,包括诸如 unsigned size = 0xffffffff ,这通常会导致编译器无法执行您希望的优化,除非您调整源代码。

    UB是允许进行某些优化的关键(例如不在循环内重做有符号数组索引的符号扩展)。看见 What Every C Programmer Should Know About Undefined Behavior #1/3