簡體   English   中英

為什么-O1比-O2快10000倍?

[英]Why -O1 is faster than -O2 for 10000 times?

以下是用於評估多項式的​​C函數:

/* Calculate a0 + a1*x + a2*x^2 + ... + an*x^n */
/* from CSAPP Ex.5.5, modified to integer version */
int poly(int a[], int x, int degree) {
  long int i;
  int result = a[0];
  int xpwr = x;
  for (i = 1; i <= degree; ++i) {
    result += a[i]*xpwr;
    xpwr *= x;
  }
  return result;
}

還有一個主要功能:

#define TIMES 100000ll
int main(void) {
  long long int i;
  unsigned long long int result = 0;
  for (i = 0; i < TIMES; ++i) {
    /* g_a is an int[10000] global variable with all elements equals to 1 */
    /* x = 2, i.e. evaluate 1 + 2 + 2^2 + ... + 2^9999 */
    result += poly(g_a, 2, 9999);
  }
  printf("%lld\n", result);
  return 0;
}

當我分別使用GCC和選項-O1和-O2編譯程序時,我發現-O1比-O2更快。

平台詳細信息:

  • i5-4600
  • 具有內核3.18的Arch Linux x86_64
  • GCC 4.9.2
  • gcc -O1 -o /tmp/a.out test.c
  • gcc -O2 -o /tmp/a.out test.c

結果:

  • 當TIMES = 100000ll時,-O1立即打印結果,而-O2需要0.36s
  • 當TIMES = 1000000000ll時,-O1在0.28s內打印結果,-O2花費的時間太長,以至於我沒有完成測試

看來-O1比-O2快大約10000倍。

當我在Mac(clang-600.0.56)上進行測試時,結果更加奇怪:-O1花費的時間不超過0.02s,即使TIMES = 1000000000000000000ll

我已經測試了以下更改:

  • 使g_a隨機(元素從1到10)
  • x = 19234(或其他一些數字)
  • 使用int代替long long int

結果是一樣的。

我嘗試查看匯編代碼,似乎-O1正在調用poly函數,而-O2進行內聯優化。 但是內聯應該使性能更好,不是嗎?

是什么使這些巨大的差異? 為什么在lang上使用-O1可以使程序這么快? -O1做錯了嗎? (我無法檢查結果,因為如果沒有優化,結果太慢了)

這是-O1main的匯編代碼:(您可以通過在gcc中添加-S選項來獲得它)

main:
.LFB12:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $9999, %edx
    movl    $2, %esi
    movl    $g_a, %edi
    call    poly
    movslq  %eax, %rdx
    movl    $100000, %eax
.L6:
    subq    $1, %rax
    jne .L6
    imulq   $100000, %rdx, %rsi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc

對於-O2

main:
.LFB12:
    .cfi_startproc
    movl    g_a(%rip), %r9d
    movl    $100000, %r8d
    xorl    %esi, %esi
    .p2align 4,,10
    .p2align 3
.L8:
    movl    $g_a+4, %eax
    movl    %r9d, %ecx
    movl    $2, %edx
    .p2align 4,,10
    .p2align 3
.L7:
    movl    (%rax), %edi
    addq    $4, %rax
    imull   %edx, %edi
    addl    %edx, %edx
    addl    %edi, %ecx
    cmpq    $g_a+40000, %rax
    jne .L7
    movslq  %ecx, %rcx
    addq    %rcx, %rsi
    subq    $1, %r8
    jne .L8
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $.LC1, %edi
    xorl    %eax, %eax
    call    printf
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc

雖然我不知道很多關於議會,很明顯, -O1只是打電話poly一次,由100000(繁衍的結果imulq $100000, %rdx, %rsi 這就是它這么快的原因。

看來gcc可以檢測到poly是一個純函數,沒有副作用。 (如果在運行poly有另一個線程修改g_a ,這將很有趣。)

另一方面, -O2內聯了poly函數,因此沒有機會將poly檢查為純函數。

我進一步做了一些研究:

我找不到執行純功能檢查的-O1使用的實際標志。

我已經分別嘗試了gcc -Q -O1 --help=optimizers列出的所有標志,但是沒有一個起作用。

可能需要將標志組合在一起才能獲得效果,但是很難嘗試所有組合。

但是我發現-O2使用的標志使效果消失,這是-finline-small-functions標志。 標志的名稱說明自己。

讓我驚訝的一件事是,您正在溢出有符號整數。 此行為在C中未定義。具體地說, int result將無法容納pow(2,9999)。 我看不出具有未定義行為的基准測試代碼有什么意義?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM