iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0
Software Development

從 C++ 菜鳥到 Low-Latency 勇者:一場分秒必爭的賽局系列 第 5

[Day 5] Advanced Modern C++ Foundations: Abstraction II

  • 分享至 

  • xImage
  •  

上一篇文的低延遲系統的大哉問中,提到了幾個在 compile time 常見且實用的語法。接下來我們將進一步拆解這些語法的運作方式,了解它們各自適合的使用情境,以及如何在實際開發中發揮作用。

1. template —— 不只是 Generic Programming

template 除了能讓同一份 code 支援不同的 type,更重要的是把原本需要 runtime 決定的資訊提前到 compile time。這也是為什麼 C++ template 常常被視為一種 compile-time programming 而不只是 generic programming

template <OrderProcessor T>
void process_order(T& processor, const Order& order) {
    processor.process(order);
}

需注意若有大量 template instantiation,可能會產生很多份不同的 machine code,造成以下問題:

  • compilation time 增加
  • object file 變大
  • binary size 增加
  • code bloat
  • instruction cache pressure

2. constexpr —— 不一定在 Compile Time 執行

很多人看到 constexpr 關鍵字就認為 constexpr function 一定在 compile time 執行,其實不然。

constexpr int square(int x) {
    return x * x;
}

int x = 10;
int y = square(x);

上述例子中的 x 是 runtime value,所以 square(x) 仍然可以在 runtime 執行。constexpr 更精確的意思是這個 function 被設計成可以在 constant evaluation 中執行。

如果 input 在 compile time 已知,那麼 compiler 就可以在 compile time 算出 y == 100

constexpr int x = 10;
constexpr int y = square(x);

constexpr 允許 compile-time evaluation,而不是強制 compile-time execution。

3. consteval —— 一定在 Compile Time 執行

如果我們真的希望某個 function 必須在 compile time 執行,可以使用 consteval

consteval int square(int x) {
    return x * x;
}

constexpr int x = square(10);  // ✅

int x;
std::cin >> x;
int y = square(x);  // ❌

因為 x 是 runtime value,而 consteval function 必須在 compile time evaluation 中完成,這使得 constexprconsteval在語意上有明確差異,因而導致程式報錯。

在 low-latency programming 裡,consteval 可以把「這個 cost 不應該出現在 runtime」變成 API contract。

4. concept —— 讓 Static Polymorphism 更容易維護

如果過度使用 templateconstexprinline,反而會降低程式的可讀性。C++20 的 concept 則可以在維持 static polymorphism 的情況下讓 interface 更清楚:

template <typename T>
concept OrderProcessor =
    requires(T processor, const Order& order) {
        processor.process(order);
    };

Runtime polymorphism 的流程如下:
interface → virtual dispatch → implementation

Static polymorphism 則有以下差異:
concept → compile-time validation→ concrete implementation → inline / optimization

concepts 讓我們可以建立 compile-time interface,把原本 runtime object hierarchy 的部分設計轉移到 compile time。

5. if constexpr —— 把 Branch 移到 Compile Time

另一個很實用的工具是 if constexpr

template <typename T>
void process(T& processor) {
    if constexpr (requires { processor.fast_path(); }) {
        processor.fast_path();
    } else {
        processor.normal_path();
    }
}

這裡的判斷不是 runtime branch,compiler 在 compile time 就知道 T 是否有 fast_path。因此最後產生的 machine code 可以只保留需要的 branch,這和 if-else statement 有本質上的不同。

普通的 if
runtime → evaluate condition → branch

if constexpr在某些情況下能讓 hot path 更簡單:
compile time → choose implementation → generate only required code

6. Compile-Time Computation 小總結

Compiler 可以直接把結果當成 constant 使用。std::array<Order, entries> orders; 中的 entries 是 compile-time constant,因此 array 的 size 在 compile time 就已經確定。這和 std::vector<Order> orders(n); 有很不同的 runtime semantics。

std::vector 的 size 是 runtime information,通常需要處理 dynamic allocation、capacity、initialization,以及可能的 growth;而固定大小的 std::array 則能讓 size 直接成為 compile-time information,減少 runtime 上需要處理的動態行為。因此,在 latency-sensitive system 中,如果資料的 size 本身就是已知的,使用 std::array<T, N> 往往比 std::vector<T> 更合理。

Compile-time computation 也常被用來建立 lookup table,將固定的計算結果預先產生,讓 runtime 只需要 lookup。不過 table 太大時可能增加 cache、TLB 與 binary size 的壓力,因此是否更快仍需根據實際 workload 衡量。


上一篇
[Day 4] Advanced Modern C++ Foundations: Abstraction I
下一篇
[Day 6] 進度存檔 | Low-Latency 新手村冒險日誌
系列文
從 C++ 菜鳥到 Low-Latency 勇者:一場分秒必爭的賽局6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言