NEO.K / PLDST程式語言設計師風格譜系
編號PLDST-008
版本v1.0
日期2026-07-30
作者Neo.K
狀態公開版/第二部核心風格原型正式論文

下載 PDF ↓回到論文索引 ↗

機器效率與人類可讀性:成本模型應寫在語言表面,還是藏在編譯器之後?

摘要

程式語言設計經常把「接近機器」與「容易閱讀」描繪成一條單軸:

機器透明、效能可控 ←────────→ 高階抽象、人類可讀

這種圖像過度簡化。C 與 C++ 讓資料布局、指標、配置與硬體操作較容易出現在程式表面,但編譯器仍依抽象機器與 as-if 原則進行轉換,表面敘述並不是實際指令序列的逐字記錄;C++ 的零額外成本原則約束特定抽象的運行時間與空間開銷,並不保證編譯時間、診斷或程式碼體積為零。Rust 以所有權、借用、迭代器與型態狀態等高階構造,試圖讓安全及可讀抽象編譯成接近手寫低階程式的結果,但其代價部分前移至型別規則、編譯器與建置時間。Go 以簡單、可讀的表面和垃圾回收降低大型團隊協作負擔,卻仍要求有性能需求的程式設計者理解配置、逃逸、資料布局與 GC 壓力。Java/HotSpot 透過直譯、分層編譯、Profile 與 Escape Analysis,允許簡潔的物件程式在 Runtime 中被重新最佳化,但暖機、去最佳化、配置移除與峰值性能不完全由來源文字直接決定。Haskell 的非嚴格語義提高組合性與抽象能力,卻可能隱藏 thunk、共享與記憶體駐留;GHC 因此提供 strictness analysis、BangPatterns 與 Profiling。Julia 則以動態語言表面和多重派發配合型別推導與專門化,並在官方效能指引中把 type stability、全域變數、配置與小型臨時陣列視為關鍵成本。[R1][R2][R3][R4][R5][R6][R7][R8]

這些案例顯示:成本不是只有「明示」或「隱藏」兩種狀態。它可以被放在:

本文提出 成本可見性與效率配置模型(Cost Visibility and Efficiency Allocation Model, CVEAM),將一項設計表示為:

P=(K,V,O,E,S,R)\mathcal{P} = ( \mathbf{K}, \mathbf{V}, \mathbf{O}, \mathbf{E}, \mathbf{S}, \mathbf{R} )

其中:

本文將效率拆成九個維度:

  1. 執行時間;
  2. 記憶體使用;
  3. 配置與回收;
  4. 資料區域性;
  5. 間接層與動態派發;
  6. 安全檢查;
  7. 排程與同步;
  8. 編譯、暖機與專門化;
  9. 尾延遲與性能變異。

核心命題是:

高階抽象⇏高運行成本\boxed{ \text{高階抽象} \not\Rightarrow \text{高運行成本} }

同時:

來源碼看似低階⇏成本完全可預測\boxed{ \text{來源碼看似低階} \not\Rightarrow \text{成本完全可預測} }

成熟設計不應讓所有機器細節污染每一行程式,也不應要求使用者相信不可觀察的「編譯器會最佳化」。更合理的原則是:

讓一般程式以意圖清楚的形式書寫;讓跨邊界、量級改變、不可攤銷、可能造成尾延遲或資源失控的成本保持可辨識;讓編譯器自由消除偶發成本,但以文件、診斷、反組譯、配置分析、Profiler 與基準測試提供可驗證證據。

關鍵詞: 程式語言設計、成本模型、零額外成本、可讀性、垃圾回收、JIT、惰性求值、型別穩定、效能可觀察性、PLDST


第一部分 效率不是一個數字

一、執行時間 KtK_t

包括:

平均速度不能取代:


二、記憶體 KmK_m

包括:

時間相近的兩個實作,可能具有完全不同的記憶體與 Cache 行為。


三、配置與回收 KaK_a

配置成本包括:

一段可讀的鏈式 API 可能:

因此「抽象語法」本身不能決定配置成本。


四、資料區域性 KlK_l

資料布局影響:

物件導向表面可能掩蓋分散配置;資料導向 API 也可能以較高表面複雜度換取連續布局。


五、間接層 KiK_i

包括:

間接層有時被 devirtualize 或 inline,有時則保留。

語言應區分:

語義上必須動態
可能被最佳化成靜態
語義上已是靜態

六、安全檢查 KbK_b

包括:

檢查可能:

安全檢查具有成本,但事故、漏洞與人工證明同樣有成本。


七、排程與同步 KsK_s

包括:

go f()async 或 Actor send 的表面短小,不代表:


八、編譯、暖機與專門化 KwK_w

包括:

若只比較穩態 throughput,會忽略 CLI、Serverless、短任務與互動式環境。


九、尾延遲與變異 KvK_v

Runtime 可能有:

平均值相近,尾延遲可能完全不同。


十、成本向量

K=(Kt,Km,Ka,Kl,Ki,Kb,Ks,Kw,Kv)\mathbf{K} = ( K_t, K_m, K_a, K_l, K_i, K_b, K_s, K_w, K_v )

PLDST 不用單一「效率分數」取代此向量。


第二部分 可讀性也不是一個數字

十一、意圖可讀性

讀者能否快速知道程式想做什麼?

高階集合操作、Iterator、Query、Pattern matching 可能比顯式 Index 與 Pointer arithmetic 更清楚。


十二、成本可讀性

讀者能否知道:

意圖可讀性與成本可讀性可能互相衝突。


十三、局部可讀性

單行程式是否容易理解。

鏈式 API、Operator overloading、Implicit conversion 可能提高局部流暢度,卻降低實際成本可見性。


十四、系統可讀性

大型系統能否推理:

局部簡潔可能增加全域不透明。


十五、診斷可讀性

當性能不符預期時,使用者能否知道:

這決定「隱藏成本」是否可治理。


第三部分 成本可以寫在哪裡

十六、語法表面

可用語法明示:

優勢:

代價:


十七、型別與效果

成本資訊可以進入:

型別能保證某些資源關係,但不一定保證實際時間。

例如 Rust 的所有權能讓複製與移動具有較明確語義,但編譯器仍可能消除實際 Memory move。


十八、API 與命名

命名可暗示成本:

clone
copy
collect
to_list
materialize
blocking
spawn
remote
unchecked
lazy
eager

API 命名是重要成本契約。

但名稱若沒有:

仍不充分。


十九、編譯器診斷

Compiler 可提供:

這讓語言表面保持清楚,同時提供機器層證據。


二十、Profiler 與 Runtime 指標

Profiler 可觀察:

其限制是:


二十一、規格與性能契約

語言或 Library 可承諾:

承諾必須區分:

語言保證
標準程式庫保證
特定實作保證
最佳化機會
經驗性結果

第四部分 六種效率—可讀風格

二十二、機器透明型

代表:

偏好:

優勢:

風險:

C 標準的抽象機器方法本身就表示:規格要求可觀察結果如同某種抽象執行,而不要求實作真的逐步使用該機制。[R1]


二十三、零額外成本抽象型

代表:

偏好:

優勢:

風險:

Stroustrup 將零額外成本概括為「不用的不付費,使用的難以手寫得更好」;Rust 官方則以 Iterator 與 typestate 等案例說明高階構造可編譯成接近直接實作的程式碼。[R2][R3]


二十四、可讀組織工程型

代表:

偏好:

優勢:

風險:

Go 的官方設計回顧同時強調來源文字清楚表達意圖,以及懂得資料表示與配置的程式設計者仍可降低 Collector 壓力。[R4]


二十五、自適應 Runtime 型

代表:

偏好:

優勢:

風險:

Oracle 的 HotSpot 文件指出,VM 只編譯性能關鍵區域;Escape Analysis 可分析物件使用範圍,並支援消除部分配置或鎖定成本。[R5]


二十六、語義抽象—嚴格度逃生型

代表:

偏好:

優勢:

風險:

Haskell 2010 明確將函式應用定義為 non-strict;GHC 文件則指出,數值內圈中消除 thunk 可能帶來巨大收益,且 Full laziness 增加共享時也可能提高記憶體駐留。[R6][R7]


二十七、推導專門化型

代表:

偏好:

優勢:

風險:

Julia 官方建議通常不必宣告回傳型別,而應撰寫讓編譯器能推導回傳型別的 type-stable function;效能指引也把非預期配置視為 type instability 或暫時陣列問題的警訊。[R8]


第五部分 代表性成本案例

二十八、C/C++:表面接近硬體仍有抽象機器

C 語言常被說成「可攜式組合語言」,但標準規格描述的是抽象機器與可觀察行為,不是固定指令序列。

這意味:

成本透明是相對的,不是逐指令同一。


二十九、C++:高階抽象依賴可消除性

RAII、Generic algorithm、Range 與容器可以使程式:

但性能結果取決於:

零額外成本不是「每個抽象在每個編譯器都永遠消失」,而是語言與 Library 的設計方向及比較原則。


三十、Rust:Ownership 同時是安全與成本語言

Rust 區分:

這讓部分成本在 API 與型別中可見。

但:

因此 Rust 提高的是成本類別可辨識性,不是精確 Cycle 可見性。


三十一、Go:簡單 Goroutine 不等於無成本並行

go f() 很短,但系統仍需要:

Effective Go 說明 Goroutine 使 Thread 建立與管理的複雜性被隱藏,但其 HTTP 範例也提醒:每個請求建立 Goroutine 時,若並行限制設計不當,仍會建立大量等待工作的 Goroutine。[R9]

因此,並行表面可讀性必須配合生命週期與負載工具。


三十二、Java:來源中的 new 不等於必然 Heap 配置

HotSpot Escape Analysis 可以判斷物件是否逃離方法或 Thread,並支援:

本文不將這些實作最佳化簡化為「Java 物件一般會被配置在 Stack」;Oracle 文件的正式重點是分析 Escape scope,並消除可被純量替代的物件配置與相關鎖。

但使用者不應將某次 JIT 結果寫成語言保證。

正確層級是:

Java semantics:建立物件語義
HotSpot implementation:可能消除物理配置
Profiler/JIT log:特定執行證據

三十三、Haskell:優雅組合可能隱藏求值圖

一個 Pipeline 可以具有高度意圖可讀性,但性能取決於:

所以 Haskell 的性能閱讀單位不只是來源控制流,也包括需求圖與 Heap profile。


三十四、Julia:同一表面因型別穩定性而分岔

Julia 中兩個看似相似的函式,若一個回傳型別穩定、另一個依資料分支回傳不同型別,Compiler 可能產生完全不同的程式碼。

這使性能成本部分位於:

Julia 的設計不是把機器成本完全藏起來,而是讓使用者透過 @code_warntype、Allocation measurement 與 Profile 觀察推導結果。


第六部分 成本可見性階梯

三十五、L0:完全隱藏

使用者看不到:

這是最低治理能力。


三十六、L1:文件化

成本只存在於:

適合非核心與可變最佳化,但容易被忽略。


三十七、L2:命名與 API 可見

例如:

clone
collect
blocking
spawn
materialize
unchecked

能讓 Code review 發現高成本操作。


三十八、L3:型別與語法可見

例如:

適合跨邊界與安全敏感成本。


三十九、L4:Compiler 可解釋

Compiler 可顯示:

適合依實作與最佳化條件改變的成本。


四十、L5:Runtime 可觀察

Profiler、Trace 與 Metrics 顯示實際負載下成本。

適合:


四十一、L6:規格化保證

例如:

此層最強,也最限制實作與演化。


第七部分 性能責任配置

四十二、語言設計者

負責:


四十三、編譯器與 Runtime

負責:


四十四、Library author

負責:


四十五、程式設計者

負責:


四十六、組織

負責:


四十七、平衡條件

Respperf(a)Control(a)+Observability(a)+Evidence(a)Resp_{perf}(a) \leq Control(a)+Observability(a)+Evidence(a)

如果要求使用者保證低延遲,卻不提供 GC、Scheduler 與配置可觀察性,責任配置失衡。


第八部分 常見失敗模式

四十八、來源碼決定論

錯誤觀念:

看起來低階,所以一定快。

忽略:


四十九、最佳化器信仰

錯誤觀念:

編譯器一定會消除。

正確態度:

可能最佳化
→ 檢查 Compiler report/assembly/profile
→ 不把它寫成語言保證

五十、抽象恐懼

錯誤觀念:

高階 Iterator、Generic 或 Object 一定慢。

事實上抽象可能:

需要證據,而不是語法印象。


五十一、性能懸崖

小改動造成量級改變:

語言應提供可解釋工具。


五十二、Debug/Release 分裂

若 Debug build 與 Release build 的性能模型差異過大:

文件與工具必須明示 Build profile。


五十三、平均值遮蔽尾延遲

GC 與 JIT 系統尤其不能只看平均 throughput。

至少分開:


五十四、微基準過度推論

微基準可能:


第九部分 設計原則

五十五、意圖優先,但成本類別不可消失

一般程式應以清楚意圖書寫;配置、阻塞、遠端、複製、生成大量工作等高影響成本應可辨識。


五十六、穩定成本寫入 API,易變成本交給工具

若成本是語義與長期契約的一部分,應在:

中明示。

若成本依最佳化器與硬體而變,應由:

呈現。


五十七、量級改變必須顯眼

從:

應有明確邊界。


五十八、最佳化是證據,不是語義

OptimizationObservedLanguageGuaranteedOptimizationObserved \neq LanguageGuaranteed

除非規格明確承諾。


五十九、抽象應可降解

高階抽象至少應提供:


六十、安全與效率不可只看 Runtime

靜態檢查增加編譯成本,卻可能降低:

應做全生命週期比較。


六十一、可讀性必須包含性能除錯

只有正常路徑好讀、性能異常時完全無法解釋,不是完整可讀設計。


第十部分 PLDST 風格判定

六十二、成本暴露指紋

Allocation visibility
Copy/move visibility
Dispatch visibility
Evaluation-order visibility
Blocking visibility
Concurrency visibility
GC visibility
Warm-up visibility
Tail-latency visibility

六十三、最佳化依賴指紋

Inlining dependence
Specialization dependence
Escape-analysis dependence
Devirtualization dependence
Fusion dependence
Strictness-analysis dependence
JIT-profile dependence

六十四、證據指紋

Complexity documentation
Compiler reports
Assembly/IR inspection
Allocation profiler
CPU profiler
Heap profiler
Runtime trace
Benchmark guidance
Production metrics

六十五、設計師比較問題

  1. 他把哪些機器成本放在語法上?
  2. 哪些放在型別或 API?
  3. 哪些交給最佳化器?
  4. 哪些交給 Runtime?
  5. 他接受多大的暖機與性能變異?
  6. 他要求使用者理解資料布局到什麼程度?
  7. 安全檢查如何付費?
  8. 抽象未被消除時如何診斷?
  9. 是否承諾零額外成本,承諾範圍是什麼?
  10. 他優先保護平均開發效率、峰值性能還是最壞情況?

第十一部分 PLDST SKILL 規格

六十六、輸入

designer
language
version
feature
source_example
compiler
runtime
build_profile
hardware
official_performance_docs

六十七、分析管線

重新網路搜尋
→ 語義成本抽取
→ 成本向量
→ 可見性階梯
→ 最佳化依賴
→ 規格保證/實作行為分離
→ 工具與證據檢查
→ 冷啟動/穩態/尾延遲分離
→ 反例與性能懸崖
→ 第二輪事實校對
→ 風格報告

六十八、JSON 雛形

{
  "mechanism": "iterator pipeline",
  "cost_vector": {
    "runtime_time": "implementation-dependent",
    "allocation": "may be eliminated",
    "dispatch": "usually static in generic form",
    "compile_time": "increased"
  },
  "visibility": {
    "source": "high-level intent",
    "type": "ownership and item type",
    "compiler_report": "recommended",
    "profile": "required for workload claim"
  },
  "guarantee_level": "design principle, not universal per-program proof",
  "performance_cliffs": [
    "dynamic dispatch",
    "lost inlining",
    "unexpected collection"
  ]
}

六十九、SKILL 禁止事項

不得:


第十二部分 限制

七十、效能依賴工作負載

同一抽象在:

可能有完全不同結果。


七十一、硬體改變設計優勢

Cache、SIMD、NUMA、GPU、記憶體與核心數量會改變最佳配置。

設計師風格分析需要標示年代。


七十二、最佳化器會演化

今日未被消除的成本,未來可能消除;反之,曾經有效的技巧可能在新版失效。

每篇個案必須重新核對版本。


七十三、可讀性具有社群性

熟悉 Iterator、Ownership、Monad 或 Multiple dispatch 的使用者,會有不同可讀判斷。


七十四、保證與經驗不能混同

PLDST 必須明確標記:

[G] language guarantee
[L] library guarantee
[I] implementation behavior
[O] optimization opportunity
[M] measured result
[A] analytical inference

第十三部分 結論

機器效率與人類可讀性不是固定的零和關係。

高階抽象可以:

低階表面也可能:

本文提出:

K=(Kt,Km,Ka,Kl,Ki,Kb,Ks,Kw,Kv)\mathbf{K} = ( K_t, K_m, K_a, K_l, K_i, K_b, K_s, K_w, K_v )

並將成本可見性分成:

完全隱藏文件API 命名型別/語法Compiler 解釋Runtime 觀察規格保證\boxed{ \text{完全隱藏} \rightarrow \text{文件} \rightarrow \text{API 命名} \rightarrow \text{型別/語法} \rightarrow \text{Compiler 解釋} \rightarrow \text{Runtime 觀察} \rightarrow \text{規格保證} }

成熟設計不要求所有成本都在來源碼逐字顯示,而要求:

  1. 跨邊界與量級改變的成本可辨識;
  2. 安全與資源關係有穩定契約;
  3. 依最佳化器而變的成本可被檢查;
  4. Runtime 行為可被 Profile 與 Trace;
  5. 性能宣稱區分語言保證、實作機會與量測結果;
  6. 高階抽象在失效時具有可解釋降解路徑。

因此,PLDST 不再只將設計師分成「重效率」或「重可讀」。更精確的問題是:

他把哪些成本視為程式語義的一部分,哪些交給型別,哪些允許編譯器消除,哪些接受 Runtime 自適應;當抽象未能被消除時,他是否提供足夠證據,讓使用者知道成本真正發生在哪裡?

最終原則為:

讓來源碼表達意圖讓高影響成本保持可辨識讓最佳化以證據而非信仰存在\boxed{ \text{讓來源碼表達意圖} \quad\land\quad \text{讓高影響成本保持可辨識} \quad\land\quad \text{讓最佳化以證據而非信仰存在} }

附錄 A 成本可見性分析卡

語言:
版本:
編譯器/Runtime:
機制:
意圖可讀性:
成本可讀性:
執行時間:
記憶體:
配置/回收:
資料區域性:
派發:
安全檢查:
排程:
編譯/暖機:
尾延遲:
來源可見:
型別可見:
API 可見:
Compiler 報告:
Runtime 指標:
規格保證:
最佳化機會:
性能懸崖:
證據:
信心:

附錄 B 來源與參考文獻

[R1] ISO/IEC JTC1/SC22/WG14, Rationale for International Standard—Programming Languages—C and ISO C working drafts.
— C 抽象機器、as-if 式可觀察語義、硬體映射與實作自由。

[R2] Bjarne Stroustrup, “Abstraction and the C++ Machine Model,” 2004/2005; “Foundations of C++,” 2012.
— C++ 機器模型、抽象與零額外成本原則。

[R3] Rust Project, The Rust Programming Language, “Performance in Loops vs. Iterators”; The Embedded Rust Book, “Zero Cost Abstractions.”
— Iterator、Closure、Typestate 與 Zero Sized Type 的最佳化案例。

[R4] Rob Pike, “Go at Google: Language Design in the Service of Software Engineering,” 2012; Go FAQ and Effective Go.
— Go 的效率、可讀性、垃圾回收、資料表示與組織工程。

[R5] Oracle, Java HotSpot Virtual Machine Performance Enhancements and Java Virtual Machine Technology Overview.
— 熱點編譯、Escape Analysis、Code cache 與 Runtime 最佳化。

[R6] Simon Marlow (ed.), Haskell 2010 Language Report.
— Haskell 非嚴格語義與 seq

[R7] GHC User’s Guide, “Optimisation” and “Bang Patterns and Strict Haskell.”
— Strictness、Thunk、Full laziness、共享與記憶體駐留。

[R8] Julia Documentation, “Performance Tips,” “Functions,” and “Profiling.”
— Type stability、回傳型別推導、配置、Function barrier 與性能工具。

[R9] Go Project, Effective Go, Goroutine and concurrency examples.
— Goroutine 隱藏 Thread 管理複雜度,以及未限制建立所形成的資源問題。


附錄 C PLDST 效能標記

[G] Language guarantee
[L] Library guarantee
[I] Implementation behavior
[O] Optimization opportunity
[M] Measured result
[A] Analytical inference

[V-S] Syntax-visible cost
[V-T] Type-visible cost
[V-A] API-visible cost
[V-C] Compiler-visible cost
[V-R] Runtime-visible cost
[V-G] Guaranteed cost

附錄 D 第二輪事實與概念校對紀錄

D.1 C 與抽象機器

已重新核對 C 標準 Rationale 與 WG14 Working Draft:


D.2 C++ 零額外成本

已重新核對 Stroustrup 的〈Abstraction and the C++ Machine Model〉與〈Foundations of C++〉:


D.3 Rust Zero-Cost Abstraction

已重新核對 Rust Book 與 Embedded Rust Book:


D.4 Go 的可讀性與 GC 成本

已重新核對〈Go at Google〉、Go FAQ 與 Effective Go:


D.5 Java/HotSpot Escape Analysis

已重新核對 Oracle HotSpot Performance Enhancements:


D.6 Haskell 與 GHC

已重新核對 Haskell 2010 Report 與 GHC 9.14 User’s Guide:

本文因此沒有將 Laziness 單向描述為慢、快、省記憶體或浪費記憶體。


D.7 Julia Type Stability

已重新核對 Julia 1.x 官方 Performance Tips、Functions、Types 與 Profiling 文件:


D.8 性能數字與版本

本文沒有固定寫入可能隨版本、硬體與 Benchmark 改變的絕對性能排行。

所有案例只用於說明:

語義保證
實作機會
診斷工具
量測證據

四者如何分離。

後續人物個案若涉及實際 Benchmark,仍須針對當次版本、Compiler、Build profile、硬體與資料集重新搜尋與測量。