當前位置: 華文星空 > 知識

為什麽在本地編譯的程式能在不同cpu上執行? 如何區別通用編譯和特別的自訂編譯?如何進行自訂編譯?

2021-03-07知識
如 C 語言在本地編譯後已經轉換為二進制程式碼了,而不同 CPU 指令集可能會發生變化,這是不是就會導致在某新 CPU 上編譯的軟體,在老舊電腦裏會因為缺少指令集而無法執行?

Alives 的回答已經提到了編譯器的預設選項很保守。我列一下 Linux 發行版的預設選項,可以對保守程度有一個直觀的認知:

  • x86-32,在 2010 年前後才提升到 i686 指令集,也就是 1995 年釋出的 Pentium Pro 的指令集。
  • 在 2018 年左右,隨著各發行版逐步放棄 32 位作業系統,x86-32 只用於 multilib,才提升到 SSE2(因為所有 x86-64 處理器都支持 SSE2),相當於 2000 年釋出的 Pentium 4 的指令集。
  • x86-64 和 x86-x32,幾乎所有發行版都采用通用指令集,即所有 x86-64 處理器支持的交集,約等於 2003 年釋出的初代 Athlon 64 的指令集(除了 3DNow!)。
  • 那麽拿到一個軟體後應該怎麽才能辨識出來什麽軟體是通用編譯的,什麽軟體是自訂編譯的?

    如果是想確認能不能正常執行,一般情況下不需要辨識。絕大多數軟體二進制釋出都是用的非常保守的指令集。少部份軟體需要充分利用新指令集的優勢,通常會編譯多個版本的庫,主程式啟動時根據 cpuid 的結果來動態載入合適的庫,新 CPU 可以充分發揮優勢,舊 CPU 也能正常執行。至於必須要用新指令集才能執行的軟體,都會在發行註記裏面寫清楚的。

    當然根據 cpuid 動態載入也有可能會因為測試覆蓋不全面出問題,尤其是遊戲,很難透過虛擬機器遮蔽指令集來測試。但這個也沒有什麽可靠的方法來辨識(否則開發者自己辨識了就不會出問題了)。比如某著名 MMORPG 最近才出過問題:

    如果是想確認軟體能不能發揮新指令集的優勢,除了發行註記裏面明確提到的,其它軟體看看動態連結庫的檔名基本上就能確認了。根據 cpuid 動態載入的,都會有類似 libxxx-generic.so libxxx-avx.so libxxx-avx2.so 這樣的命名。

    應該如何進行自訂編譯?

    GCC/Clang -march 和一大堆 -m 的選項(如 -msse3 -mavx )來控制采用哪些指令集。(註意 GCC 的 x86 包括 x86-32、x86-64、x86-x32。)

    最簡單的方法就是直接用 -march=native ,讓編譯器自己根據 cpuid 開關選項。如果想知道 -march=native 開了哪些選項,執行

    gcc -c -Q -march=native --help=target

    MSVC 的控制沒有這麽精細,但也有 /arch:SSE /arch:SSE2 /arch:AVX /arch:AVX2 /arch:AVX512 這幾個選項。