編譯器內建函式與位元運算

最後更新: 2022-03-03 23:02:53 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

GCC 與 Clang 會針對其前端可直接辨識的運算提供內建函式形式。位元計數內建函式可以根據選取的目標與最佳化內容,降低為目標指令、函式庫指令序列或最佳化後的運算式。原始碼應選擇能夠明確表達其可攜性與零輸入契約的介面。

GNU 風格的位元內建函式 #

GCC 與 Clang 支援以下適用於 unsigned int、unsigned long 與 unsigned long long 的函式族:

操作unsigned intunsigned longunsigned long long
設定位元數量__builtin_popcount__builtin_popcountl__builtin_popcountll
前導零位元數量__builtin_clz__builtin_clzl__builtin_clzll
尾端零位元數量__builtin_ctz__builtin_ctzl__builtin_ctzll

回傳型別為 int;尾綴用來選擇引數寬度。當邏輯寬度很重要時,請明確進行轉型,而不要依賴整數提升。常見 ABI 對 unsigned long 使用不同寬度,因此當演算法要求特定寬度時,請對 unsigned long long 值使用 ll 形式,或使用固定寬度的標準函式。

__builtin_clz(0) 與 __builtin_ctz(0) 會產生未定義行為。如果輸入可能為零,請先進行檢查:

cpp
#include <limits>

unsigned trailing_or_width(unsigned value) {
    return value == 0 ? std::numeric_limits<unsigned>::digits
                      : static_cast<unsigned>(__builtin_ctz(value));
}

popcount(0) 已有定義,並會回傳零;但是,使用一致的標準介面可以避免為相關操作建立不同的契約。

可攜式 C++ 優先使用 C++20 <bit> #

C++20 在 <bit> 中為無號整數型別提供 std::popcount、std::countl_zero 與 std::countr_zero。不同於 GNU 的前導零與尾端零內建函式,這些計數函式將零輸入時的結果定義為該型別的值位元數量:

cpp
#include <bit>
#include <cstdint>

constexpr std::uint32_t bits = 0b101101;
static_assert(std::popcount(bits) == 4);
static_assert(std::countl_zero(std::uint32_t{0}) == 32);
static_assert(std::countr_zero(std::uint32_t{16}) == 4);

如果必須支援函式庫功能邊界,請在包含 <bit> 後檢查 __cpp_lib_bitops;C++20 為此功能規定的值為 201907L。對於編譯器特定的程式碼,只有在先保護其使用後,才能透過 __has_builtin 檢查內建函式是否可用:

cpp
#if defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L
// Use std::popcount, std::countl_zero, and std::countr_zero.
#elif defined(__has_builtin)
#  if __has_builtin(__builtin_popcount)
#    define PROJECT_HAS_BUILTIN_POPCOUNT 1
#  endif
#endif

GCC 與 Clang 都有記錄這些特定的內建函式,因此明確支援兩者的專案也可以集中處理編譯器家族檢查,並為其他編譯器保留可攜式的替代方案。不要假設 __has_builtin 一定存在,也不要使用它來選擇零值前置條件未由公開 API 強制執行的內建函式。

程式碼產生與常數求值 #

對於效能關鍵的目標,請檢查產生的程式碼,因為內建函式的寫法並不保證會產生單一指令。硬體支援、目標功能、運算元寬度、周圍的控制流程與最佳化等級會共同決定降低結果。符合標準的實作可以為標準 <bit> 函式提供等價的語意資訊給最佳化器,同時保留可攜性;當其運算元與函式庫實作允許時,也能用於常數求值。對於 C++20 以前的函式庫,或經測量確認的目標差異,可以採用內建函式替代方案;但該方案應置於單一、能感知寬度且具有已文件化前置條件的包裝器後方。

評論

在 GitHub 上閱讀或評論(發表評論需要登入)