日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

GCC __builtin_expect与kernel指令序列优化

發布時間:2024/4/18 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 GCC __builtin_expect与kernel指令序列优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

例題描述

例題描述:通過C語言識別一個int型數據在十進制下是否為回文數字。不能有額外的字符串空間開銷。
如:2156512是回文數,而21565不是回文數。

問題分析:
1. 當這個數字是負數的時候,肯定不是回文數
2. 可以將這個數翻轉,判斷翻轉后是否相同

C語言代碼演示:

/*************************************************************************> File Name: isPalindrome.cpp> Author: ChenXiansen > Mail: 1494089474@qq.com > Created Time: Wed 11 Nov 2020 09:49:43 AM CST************************************************************************/#include <stdio.h>bool isPalindrome(int x, int n) {if (__builtin_expect(!!(x < 0), 0)) return false;int y = 0, z = x;while (x) {y = y * n + x % n;x /= n;}return z == y; }int main() {int n, Cov = 10;scanf("%d", &n);if (isPalindrome(n, Cov)) {printf("Num: %d in Cov: %d is a reverse num!\n", n, Cov);} else {printf("NO! It's not a reverse num!\n");}return 0; }

代碼分析

在 isPalindrome函數中,使用了if (__builtin_expect(!!(x < 0), 0)) return false; 這條語句。
下面是對__builtin_expect宏的表述:

GCC提供了__builtin_expect宏,作為編譯分支時候的暗示。用法是__builtin_expect(var, expected_value),也就是說,告訴編譯器var這個變量的值比較可能是什么。在kernel中這個宏被用在likely和unlikely這兩個宏定義中:

#define likely(x) __builtin_expect(!!(x), 1) //x很可能成立 #define unlikely(x) __builtin_expect(!!(x), 0) //x很可能不成立

從現代的處理器架構說起。相信大家都知道流水線技術,就是CPU可以在統一個時鐘周期內同時執行多條指令,當前指令尚未執行完畢,實際上就已經開始處理后面的指令了。然而當處理器遇到分支的時候,就無法判斷即將執行的是哪個分支,流水線優化就受到了限制。

后來,隨著處理器技術的發展,處理器開始直接預取分支后面的指令,如果發現分支預判錯誤,則拋棄之前的執行結果,重新轉入正確的分支繼續執行。 更加現代的處理器甚至能夠預取更多后面的指令,對于不依賴之前執行結果的指令都可以按照一定的規則預先執行得到結果。


匯編代碼

1. 初始代碼匯編

接下來在終端運行gcc -S isPalindrome.cpp,查看匯編代碼中的isPalindrome()函數:

.LFB0:.cfi_startprocendbr64pushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6movl %edi, -20(%rbp)movl %esi, -24(%rbp)movl -20(%rbp), %eaxshrl $31, %eaxmovzbl %al, %eaxtestq %rax, %raxje .L2movl $0, %eaxjmp .L3 .L2:movl $0, -8(%rbp)movl -20(%rbp), %eaxmovl %eax, -4(%rbp) .L5:cmpl $0, -20(%rbp)je .L4movl -8(%rbp), %eaximull -24(%rbp), %eaxmovl %eax, %ecxmovl -20(%rbp), %eaxcltdidivl -24(%rbp)movl %edx, %eaxaddl %ecx, %eaxmovl %eax, -8(%rbp)movl -20(%rbp), %eaxcltdidivl -24(%rbp)movl %eax, -20(%rbp)jmp .L5 .L4:movl -4(%rbp), %eaxcmpl -8(%rbp), %eaxsete %al .L3:popq %rbp.cfi_def_cfa 7, 8ret.cfi_endproc .LFE0:.size _Z12isPalindromeii, .-_Z12isPalindromeii.section .rodata

對.LFB0段的釋義:

  • main()函數中的變量n的值存入了edi寄存器,作為bool isPalindrome(int x, int n)的形參x。
  • main()函數中的變量Cov的值存入esi寄存器,作為bool isPalindrome(int x, int n)的形參n。
  • movl %edi, -20(%rbp)
    movl %esi, -24(%rbp)
    將這兩個參數壓入函數棧。
  • movl -20(%rbp), %eax :將存在于-20(%rbp)中x的值,存入eax寄存器。
  • 執行以下匯編指令,這段匯編指令由if (__builtin_expect(!!(x < 0), 0)) return false;轉換得到: shrl $31, %eaxmovzbl %al, %eaxtestq %rax, %raxje .L2movl $0, %eaxjmp .L3 可以看到:在執行前三行語句后(這三行代碼現在沒有弄清楚具體含義,待后續查閱相關資料),下一步是判斷分支:匯編代碼是je .L2,
  • 如果滿足testq %rax, %rax條件,下一步是執行C程序中的:int y = 0, z = x; 對應在匯編.L2函數中的: .L2:movl $0, -8(%rbp)movl -20(%rbp), %eaxmovl %eax, -4(%rbp)
  • 如果不滿足條件,將返回值0放入寄存器eax中,執行.L3 匯編函數部分的return操作。
  • 修改程序后匯編:

    現在將

    if (__builtin_expect(!!(x < 0), 0)) return false;

    這部分C代碼轉換成

    if (x < 0) return false;

    觀察執行預處理-編譯-匯編過程后,查看匯編代碼中的isPalindrome()函數。(由于.L2之后的部分完全相同,因此沒有在博客中顯示)

    .LFB0:.cfi_startprocendbr64pushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6movl %edi, -20(%rbp)movl %esi, -24(%rbp)cmpl $0, -20(%rbp)jns .L2movl $0, %eaxjmp .L3

    可以看到:將函數參數全部壓棧后,進行的分支判斷:

    movl %edi, -20(%rbp)movl %esi, -24(%rbp)cmpl $0, -20(%rbp)jns .L2movl $0, %eaxjmp .L3

    cmpl $0, -20(%rbp):如果 bool isPalindrome(int x, int n)函數中 參數x的值不滿足 x < 0,則執行.L2之后的匯編函數,反之直接return 0.


    參考資料:
    https://zhuanlan.zhihu.com/p/27339191
    http://deltamaster.is-programmer.com/posts/37285.html

    附錄:完整匯編程序代碼:

    .file "isPalindrome.cpp".text.globl _Z12isPalindromeii.type _Z12isPalindromeii, @function _Z12isPalindromeii: .LFB0:.cfi_startprocendbr64pushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6movl %edi, -20(%rbp)movl %esi, -24(%rbp)movl -20(%rbp), %eaxshrl $31, %eaxmovzbl %al, %eaxtestq %rax, %raxje .L2movl $0, %eaxjmp .L3 .L2:movl $0, -8(%rbp)movl -20(%rbp), %eaxmovl %eax, -4(%rbp) .L5:cmpl $0, -20(%rbp)je .L4movl -8(%rbp), %eaximull -24(%rbp), %eaxmovl %eax, %ecxmovl -20(%rbp), %eaxcltdidivl -24(%rbp)movl %edx, %eaxaddl %ecx, %eaxmovl %eax, -8(%rbp)movl -20(%rbp), %eaxcltdidivl -24(%rbp)movl %eax, -20(%rbp)jmp .L5 .L4:movl -4(%rbp), %eaxcmpl -8(%rbp), %eaxsete %al .L3:popq %rbp.cfi_def_cfa 7, 8ret.cfi_endproc .LFE0:.size _Z12isPalindromeii, .-_Z12isPalindromeii.section .rodata .LC0:.string "%d".align 8 .LC1:.string "Num: %d in Cov: %d is a reverse num!\n" .LC2:.string "NO! It's not a reverse num!".text.globl main.type main, @function main: .LFB1:.cfi_startprocendbr64pushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6subq $16, %rspmovq %fs:40, %raxmovq %rax, -8(%rbp)xorl %eax, %eaxmovl $10, -12(%rbp)leaq -16(%rbp), %raxmovq %rax, %rsileaq .LC0(%rip), %rdimovl $0, %eaxcall __isoc99_scanf@PLTmovl -16(%rbp), %eaxmovl -12(%rbp), %edxmovl %edx, %esimovl %eax, %edicall _Z12isPalindromeiitestb %al, %alje .L7movl -16(%rbp), %eaxmovl -12(%rbp), %edxmovl %eax, %esileaq .LC1(%rip), %rdimovl $0, %eaxcall printf@PLTjmp .L8 .L7:leaq .LC2(%rip), %rdicall puts@PLT .L8:movl $0, %eaxmovq -8(%rbp), %rcxxorq %fs:40, %rcxje .L10call __stack_chk_fail@PLT .L10:leave.cfi_def_cfa 7, 8ret.cfi_endproc .LFE1:.size main, .-main.ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0".section .note.GNU-stack,"",@progbits.section .note.gnu.property,"a".align 8.long 1f - 0f.long 4f - 1f.long 5 0:.string "GNU" 1:.align 8.long 0xc0000002.long 3f - 2f 2:.long 0x3 3:.align 8 4:

    總結

    以上是生活随笔為你收集整理的GCC __builtin_expect与kernel指令序列优化的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。