後端工坊 2026 年 4 月 27 日

2026-04-27 — Rust 1.95.0 cfg_select!、Go 1.26 型別建構循環偵測、Linux 最快時間戳

Rust 1.95.0 釋出:cfg_select! 巨集、…

Rust 1.95.0 釋出:cfg_select! 巨集、if-let 守衛、原子操作新 API

The Rust Blog · 2026-04-16

Rust 1.95.0 於 2026 年 4 月 16 日發布,帶來數項語言層面的改動與 API 穩定化。以下逐條說明本版本的技術細節。

cfg_select! 巨集

cfg_select! 是一個新的編譯期條件巨集,語意類似 match 陳述式,但匹配的是 cfg 謂詞(configuration predicates)。它將先前只能透過外部 crate cfg-if 實現的功能內建進標準函式庫,語法與 cfg-if 不同但功能相等。開發者現在可以用以下形式撰寫平台條件碼:

cfg_select! {
    [target_os = "linux"] => { /* Linux 特定實作 */ }
    [target_os = "windows"] => { /* Windows 特定實作 */ }
    _ => { /* 其他平台預設值 */ }
}

Match 運算式中的 if-let 守衛

延伸 1.88 版穩定的 let chains 功能,1.95.0 讓 match 模式的守衛(guard)可內嵌 if let

match value {
    Some(x) if let Ok(y) = compute(x) => { use(y) }
    _ => {}
}

需注意:if let 守衛中的模式綁定不參與 match 的窮盡性(exhaustiveness)檢查,這是語意上刻意的選擇,避免規則變得過於複雜。

穩定化的 API

記憶體與型別轉換:MaybeUninitCell 的陣列版 FromAsRefAsMut 實作;bool: TryFrom<{integer}> 整數轉布林。

原子操作:AtomicPtrAtomicBoolAtomicIsizeAtomicUsize 取得 updatetry_update 方法,可在不寫全 compare-exchange loop 的情況下進行 fetch-modify-store 操作。

核心工具:core::range 模組引入 RangeInclusive 及迭代器支援;core::hint::cold_path 用於向最佳化器標記冷路徑;不安全指標方法 as_ref_uncheckedas_mut_unchecked 穩定化。

集合操作:VecVecDequeLinkedList 的可變參考方法(push_mutinsert_mut 等)穩定化;Layout 新增 dangling_ptrrepeatrepeat_packedextend_packed

const 環境:fmt::from_fnControlFlow::is_breakControlFlow::is_continue 現可在 const 環境中使用。

破壞性變更:JSON 目標規格去穩定化

自 1.95.0 起,透過 JSON 自訂目標規格(custom target specifications)的功能在穩定版 Rust 上不再支援,需使用 nightly 版本的 -Z build-std 特性才能為自訂目標建構標準函式庫。此舉影響嵌入式開發者與裸機目標的工具鏈設定,需要遷移至 nightly 工具鏈或等待官方支援。

原始來源:Rust Blog – Announcing Rust 1.95.0


Go 1.26 型別建構重構:循環偵測的演算法改進

The Go Blog · 2026-03-24

Go 1.26 的型別檢查器(type checker)進行了一項重大內部重構:以系統化的循環偵測(cycle detection)機制取代舊有複雜的型別建構流程。此改動對一般 Go 使用者不可見,但修復了多個編譯器崩潰問題(#75918、#76383、#76384、#76478 等)。

型別建構的基本模型

型別檢查器在處理每個型別時,會建立對應的內部表示(Defined struct),並追蹤兩種狀態:建構中(Under Construction)——尚未完整、欄位未填滿;完成(Complete)——所有欄位已填齊、可安全解構。型別以深度優先(depth-first)順序完成,依賴項先完成,再由依賴它的型別完成。

簡單遞迴型別如 type T []U; type U *T 可同時完成——因為 *T 只需知道 T 的「指標性」(pointerness),不需解構其完整內部結構。

循環問題:unsafe.Sizeof 案例

問題出現在:

type T [unsafe.Sizeof(T{})]int

此處 T{} 是一個「上游(upstream)」——對未完成型別 T 的型別轉換。計算陣列長度需要 T 完成,但 T 的完成又需要陣列長度——形成死鎖。

上游偵測策略

舊方法在「下游運算子」偵測循環(複雜且難以窮舉);新方法改在「上游」——即產生不完整值的起點——進行偵測。上游包括:型別轉換 T()、回傳不完整型別的函式呼叫 f()、型別斷言 i.(T)、channel 接收 <-(make(<-chan T))、map 存取 make(map[int]T)[42]*new(T)。而 new(T)(所有指標同尺寸)等操作不需解構,屬於安全操作,不納入偵測。

實作模式

func callExpr(call *syntax.CallExpr) operand {
  x := typeOrValue(call.Fun)
  switch x.mode() {
  case typeExpr:
    T := x.typ()
    if !isComplete(T) {
      reportCycleErr(T)
      return invalid
    }
    // 安全解構 T
  }
}

此重構讓型別檢查器的循環偵測邏輯變得可枚舉、可測試,消除了多個邊緣案例下的編譯器恐慌(panic)。

原始來源:Go Blog – Type Construction and Cycle Detection


Linux 最快時間戳:TSC、vDSO 與快取策略的效能剖析

hmpcabral.com · 2026-04-26

在延遲預算為 1–10 微秒的低延遲分散式追蹤管線中,初始以 std::chrono 實作的時間戳方案每次呼叫耗費約 47ns——幾乎消耗了整個預算。本文對 Linux 時間戳機制進行系統性剖析。

主要時脈來源

TSC(Timestamp Counter):64 位元計數器,在現代 CPU 上以不變速率遞增。Intel 文件定義「Invariant TSC 在所有 ACPI P-、C-、T-state 下以固定速率運行」。讀取方式為 lfence; rdtsc,約 25–32 個時脈週期。需要核心頻率估算才能準確轉換為奈秒。

vDSO(Virtual Dynamically Shared Object):核心將時間資料頁映射至使用者空間,避免 syscall 的 context switch 開銷。資料頁包含:當前時間估算值、TSC 計數值、用於轉換的乘數/位移量。以 seqlock 保護,防止核心更新時讀到不一致資料。

基準測試結果

實作方案中位數 (ns)改善幅度
原始 3× clock_gettime()47.2基準線
TscTimer(週期估算)28.340%
VdsoTimer(核心頻率)20.557%
TscCacheTimer(快取)19.658%
VdsoCacheTimer(快取)20.058%

尾延遲問題

標準 vDSO 方案在核心更新資料頁時會出現約 200ns 的尾延遲,原因是:seqlock 自旋等待更新完成,以及 L1/L2 快取失中(amortized 後每 100 次呼叫中約有 100× 的代價)。

解決策略

直接讀取 TSC:rdtsc() 取代單調時鐘呼叫,消除 vDSO 開銷。最小化換算:只計算 TSC 差值乘法,跳過奈秒正規化。繞過 vDSO:透過 ELF 輔助向量 AT_SYSINFO_EHDR 直接存取資料頁。快取策略:預快取乘數/位移值,僅以可接受頻率(≤HZ=1000)刷新,徹底消除尾延遲。

需注意:繞過 vDSO 需要核心版本特定的資料頁佈局知識(Linux 6.15 後有所改變)。推薦方案 TscCacheTimer 在 1μs 任務上的額外開銷僅 2%,相比初始方案的 4.3% 有顯著改善。

原始來源:hmpcabral.com – The Fastest Linux Timestamps


End of article
0
Would love your thoughts, please comment.x
()
x