【问题标题】:Where the const reduce performance rather than optimizing it?const 在哪里降低性能而不是优化它?
【发布时间】:2018-04-26 04:36:02
【问题描述】:

请看下面的代码sn-p

#define HF_ND_SZ sizeof(struct huffman_node)
#define TSIZE_MAX 256

struct huffman_node * build_decomp_huffman_tree(uint64_t *table, int size) {
    static struct huffman_node huffman_node_list2[TSIZE_MAX * 3];
    int i = 0, j = 0;
    int k = TSIZE_MAX * 2; // this is the case point 1
    //...//
    for (i = 0; i < size - 1; i++) {
        huffman_node_list2[k + i] = huffman_node_list2[i + 1]; // point 2
        huffman_node_list2[TSIZE_MAX + i].right = &huffman_node_list2[k+ i];
    // ... //
    }
    return &huffman_node_list2[size - 1];
}

为简单起见,我将代码减少并指出了我要突出显示的位置,也不要把算法和结构想得太深。

我想要的是,如果我们将 point 1 定义为 const int k = TSIZE_MAX * 2;,那么在 point 2 或 3 是否会发生任何优化,其中分配发生在连续数据上(数组)huffman_node_list2[k + i] = huffman_node_list2[i + 1]; ?

(如果我的假设是错误的,请容忍并纠正我的假设,我认为当我们在本地或全局范围内声明const 时,如果我们使用该不可变内存,它被创建为不可变内存分配> 并在循环结构中执行 数学运算,如 第 2 点或第 3 点([k + i]),在运行时程序必须加载不可变内存循环的每次迭代并将结果存储在临时内存位置,如果不可变内存有大块怎么办,希望你能抓住我的想法,我是正确的吗?)

【问题讨论】:

  • 是有实际问题,还是只是为了学习而问? const 对于实体​​意味着编译器将不允许修改它的代码,但它可以很好地存储在可修改的内存中。即使它存储在不可修改的内存中(从进程的角度来看 - 区别仅在于内核将该内存页面设为只读),它也不需要先复制到其他地方,所以我看到const 没有理由让事情变慢。可修改的内存也被加载到寄存器中以便快速处理。
  • const 的目的不是让代码更快,而是让代码更正确。当您标记 const 时,您是在要求编译器(通过错误)告诉您是否/何时尝试修改它。
  • const 根本不需要创建内存位置,除非您获取它的地址。更有可能huffman_node_list2[k + i] = huffman_node_list2[i + 1] 被编译为huffman_node_list2[TSIZE_MAX * 2 + i] = huffman_node_list2[i + 1],其中不仅TSIZE_MAX * 2 在编译时被评估,而且huffman_node_list2+TSIZE_MAX*2 也是如此,如果你明白我的意思的话。
  • @SirGuy 修改声明为const 的变量是UB,所以不,它总是正确的,标记为const 的变量不能被修改。 const_cast 仅在诸如 const 参考之类的东西上是合法的
  • 另外,你必须决定你问的是C还是C++作为@987654338的真正含义@ 两者之间有很大不同!

标签: c optimization constants compiler-optimization


【解决方案1】:

const 可能如果编译器将它放在只读 .text 部分足够远导致缓存未命中。

这可能发生在全局 consts 或者当编译器将其从函数中提升出来而不是必须使用指令构建它时(结构或数组的相当常见的优化)如果多个函数使用相同的常数,但也增加了与代码的距离,从而导致未命中的可能性。

由于您没有使用任何聚合类型,因此与体面的优化编译器应该没有区别。

有一篇关于如何布置不同数据的好文章here

【讨论】:

    【解决方案2】:

    使用 Visual C,我编译了您的代码的两个版本:使用 const int k 和不使用 const。标志/FA 在(某些)人类可读的.asm 文件中生成代码机器。没有使用优化标志。

    结果是:没有优化,没有区别。产生的机器码是完全一样的:

    ; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.24231.0 
    
        TITLE   opt_const.c
        .686P
        .XMM
        include listing.inc
        .model  flat
    
    INCLUDELIB LIBCMT
    INCLUDELIB OLDNAMES
    
    PUBLIC  _main
    _BSS    SEGMENT
    ?huffman_node_list2@?1??main@@9@9 DB 01fd4H DUP (?) ; `main'::`2'::huffman_node_list2
    _BSS    ENDS
    ; Function compile flags: /Odtp
    ; File c:\joël\tests\opt_const.c
    _TEXT   SEGMENT
    _j$ = -16                       ; size = 4
    _size$ = -12                        ; size = 4
    _k$ = -8                        ; size = 4
    _i$ = -4                        ; size = 4
    _argc$ = 8                      ; size = 4
    _argv$ = 12                     ; size = 4
    _main   PROC
    
    ; 10   : {
    
        push    ebp
        mov ebp, esp
        sub esp, 16                 ; 00000010H
        push    esi
        push    edi
    
    ; 11   :     static struct huffman_node huffman_node_list2[TSIZE_MAX * 3];
    ; 12   :     int i = 0, j = 0, size = 17;
    
        mov DWORD PTR _i$[ebp], 0
        mov DWORD PTR _j$[ebp], 0
        mov DWORD PTR _size$[ebp], 17       ; 00000011H
    
    ; 13   :     int k = TSIZE_MAX * 2; // this is the case point 1
    
        mov DWORD PTR _k$[ebp], 194         ; 000000c2H
    
    ; 14   :     //...//
    ; 15   :     for (i = 0; i < size - 1; i++) {
    
        mov DWORD PTR _i$[ebp], 0
        jmp SHORT $LN4@main
    $LN2@main:
        mov eax, DWORD PTR _i$[ebp]
        add eax, 1
        mov DWORD PTR _i$[ebp], eax
    $LN4@main:
        mov ecx, DWORD PTR _size$[ebp]
        sub ecx, 1
        cmp DWORD PTR _i$[ebp], ecx
        jge SHORT $LN3@main
    
    ; 16   :         huffman_node_list2[k + i] = huffman_node_list2[i + 1]; // point 2
    
        mov edx, DWORD PTR _i$[ebp]
        add edx, 1
        imul    esi, edx, 28
        add esi, OFFSET ?huffman_node_list2@?1??main@@9@9
        mov eax, DWORD PTR _k$[ebp]
        add eax, DWORD PTR _i$[ebp]
        imul    edi, eax, 28
        add edi, OFFSET ?huffman_node_list2@?1??main@@9@9
        mov ecx, 7
        rep movsd
    
    ; 17   :         huffman_node_list2[TSIZE_MAX + i].right = &huffman_node_list2[k+ i];
    
        mov ecx, DWORD PTR _k$[ebp]
        add ecx, DWORD PTR _i$[ebp]
        imul    edx, ecx, 28
        add edx, OFFSET ?huffman_node_list2@?1??main@@9@9
        mov eax, DWORD PTR _i$[ebp]
        add eax, 97                 ; 00000061H
        imul    ecx, eax, 28
        mov DWORD PTR ?huffman_node_list2@?1??main@@9@9[ecx], edx
    
    ; 18   :     // ... //
    ; 19   :     }
    
        jmp SHORT $LN2@main
    $LN3@main:
    
    ; 20   :     return 0;
    
        xor eax, eax
    
    ; 21   : }
    
        pop edi
        pop esi
        mov esp, ebp
        pop ebp
        ret 0
    _main   ENDP
    _TEXT   ENDS
    END
    

    编辑:我用 gcc,-O3 优化标志做了同样的测试。 而且...同样的结果:生成的汇编代码在有和没有const 关键字的情况下再次严格相同。

            .file       "opt_const.c"
            .section    .text.unlikely,"ax",@progbits
    .LCOLDB0:
            .section    .text.startup,"ax",@progbits
    .LHOTB0:
            .p2align 4,,15
            .globl      main
            .type       main, @function
    main:
    .LFB23:
            .cfi_startproc
            movl        $huffman_node_list2.2488+16384, %eax
            .p2align 4,,10
            .p2align 3
    .L2:
            movq        -16352(%rax), %rdx
            movq        %rax, -8192(%rax)
            addq        $32, %rax
            movq        %rdx, -32(%rax)
            movq        -16376(%rax), %rdx
            movq        %rdx, -24(%rax)
            movq        -16368(%rax), %rdx
            movq        %rdx, -16(%rax)
            movq        -16360(%rax), %rdx
            movq        %rdx, -8(%rax)
            cmpq        $huffman_node_list2.2488+17088, %rax
            jne .L2
            xorl        %eax, %eax
            ret
            .cfi_endproc
    .LFE23:
            .size       main, .-main
            .section    .text.unlikely
    .LCOLDE0:
            .section    .text.startup
    .LHOTE0:
            .local      huffman_node_list2.2488
            .comm       huffman_node_list2.2488,24576,32
            .ident      "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609"
            .section    .note.GNU-stack,"",@progbits
    

    【讨论】:

    • 在我看来,Visual C 对 C 编译器来说是一个糟糕的借口,但这并不会使这个答案变得糟糕。但是激活了哪些优化,这在分析中可能更重要。
    • 这个程序集是在优化的情况下生成的吗?
    • 仅针对 GCC 测试开启了优化。
    【解决方案3】:

    const 根本不需要创建内存位置,除非您获取它的地址。它们可以作为立即模式常量消失在指令流中,或者在编译或链接时添加到地址中。

    例如,huffman_node_list2[k + i] = huffman_node_list2[i + 1] 几乎可以肯定编译为 huffman_node_list2[TSIZE_MAX * 2 + i] = huffman_node_list2[i + 1],其中不仅TSIZE_MAX * 2 在编译时评估,huffman_node_list2+TSIZE_MAX*2 在链接时评估。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-28
      • 2021-01-06
      • 2011-07-30
      • 1970-01-01
      • 2020-06-02
      • 2012-10-05
      • 2020-05-29
      • 1970-01-01
      相关资源
      最近更新 更多