工程趣聞 2026 年 8 月 24 日

2026-08-24 — CMake 硬跑 GPT-2、Apple II 接上 90 年代柯達相機、WebGPU pipeline 塞進一張 PNG

primary=https://github.com/AlpinDale/gpt2.cmake primary=https://www.colino.net/wordpress/archives/2026/08/23/kodak-dc50-now-usable-on-the-apple-ii/ primary=https://hugodaniel.com/posts/declarative-webgpu-with-s-expressions/

當建置腳本自己會推理:有人用 CMake 硬幹出一顆 GPT-2

GitHub · 2026-08-23

開發者 AlpinDale 在 2026 年 8 月 23 日於 GitHub 發布 gpt2.cmake,把 OpenAI 的 GPT-2 語言模型完全用 CMake 的腳本語言重寫了一遍。這不是用 CMake 去呼叫外部程式跑推理,而是直接拿 CMake 內建的 math(EXPR)foreachstring() 等指令當成一台通用計算機,把分詞、矩陣運算、自迴歸生成整條推理管線都寫成 CMakeLists 語法。專案目前只有一個 commit、5 顆星,採用 BSD 3-Clause 授權。

動機

CMake 原本的設計目的是讀 CMakeLists.txt、決定要編譯哪些檔案、要不要開某個 flag,從沒打算被拿來做浮點運算或跑類神經網路。這正是 gpt2.cmake 的笑點所在:只要一個語言有變數、迴圈、條件判斷,它理論上就是圖靈完備(Turing complete)的,什麼都能算,包括 transformer 推理——即使慢到令人髮指。「用不該拿來寫程式的語言硬跑出圖靈完備應用」在工程圈是一個長青傳統,PowerPoint 動畫、Excel 都曾被拿來跑西洋棋引擎,這次輪到 CMake。

實作方式

因為 CMake 沒有浮點型別,gpt2.cmakeQ16.16 定點數(fixed-point)表示所有權重與中間值——把一個數字拆成 16 個位元存整數部分、16 個位元存小數部分,乘除法靠整數運算模擬小數點位移,是早期沒有 FPU 硬體上常見的老招數。專案用 Python 工具把 GPT-2 的 vocab、merges 與權重轉成可被 include() 進來的 CMake 腳本,分詞則是逐字元用 string(FIND) 在 vocab 字串裡查位置:

string(LENGTH "${PROMPT}" _plen)
math(EXPR _plm1 "${_plen} - 1")
foreach(_i RANGE 0 ${_plm1})
  string(SUBSTRING "${PROMPT}" ${_i} 1 _ch)
  string(FIND "${VOCAB}" "${_ch}" _id)
  list(APPEND _ids ${_id})
endforeach()

自迴歸生成迴圈則是每一步呼叫 gpt2_logits() 算出下一個 token 的分數,再用 fx_argmax() 取機率最大的字,寫法跟平常 Python 裡的生成迴圈概念上一模一樣,只是換了一個完全不適合的語言載體:

foreach(_k RANGE 1 ${N})
  gpt2_logits(_logits "${_ids}")
  fx_argmax(_next _logits ${N_VOCAB})
  list(APPEND _ids ${_next})
endforeach()
  • cmake -P gpt2.cmake — 跑內建的小型玩具模型,方便快速驗證邏輯
  • cmake -P gpt2_full.cmake -DPROMPT="Hello" -DN=2 — 從 Hugging Face 下載真正的 GPT-2 權重來推理

限制與效果

真正吃運算量的矩陣乘法與注意力機制被放在另外 include() 進來的檔案裡,靠 foreach 巢狀迴圈搭配 math(EXPR) 一格一格算,CMake 直譯器完全不是為這種工作負載設計的。這也是為什麼作者範例參數只給 -DN=2,也就是只生成兩個 token 而已——速度可想而知。專案沒有附任何效能數據,GitHub 上零 fork、零 issue,顯然是一次性的技術玩笑或概念驗證,而不是打算被正式使用的工具。

原始來源:AlpinDale/gpt2.cmake (GitHub)


幫 30 年前的柯達相機找到新語言:讓 Kodak DC50 在 Apple II 上開口說話

colino.net · 2026-08-23

法國開發者 Colin Leroy 在 2026 年 8 月 23 日更新了他的「Quicktake for Apple II」專案,加入對 1996 年上市的 Kodak DC50 Zoom 數位相機的支援,讓 1983 年推出的 Apple IIe 能直接讀取、預覽、刪除這台相機裡的照片。這套軟體原本只服務更早的 Apple QuickTake 150,這次是把整套解碼與傳輸邏輯擴充到一台完全不同世代、不同廠牌的相機上。

原本的問題

DC50 用 115,200 bps 高速序列埠,搭配 PCMCIA 記憶卡(Leroy 手上這台配 6MB 卡,可存 92 張低畫質或 36 張高畫質照片),解析度 756×504,影像用柯達自家的 RADC 格式壓縮。RADC 不是像 JPEG 那種區塊離散餘弦轉換,而是用一顆決策樹先決定要讀多少 bit 的 token,再拿前一個像素的值當預測基準、只編碼與預測值的差量——概念上接近後來影片編碼常見的差分編碼,但年代更早、格式也完全沒有公開文件。連相機的基本控制指令集柯達都沒有釋出過。

採用的方法

Leroy 從三個管道逆向工程出協定與格式:開源解碼器 dcrawlibgphoto2 裡對同家族相機的既有處理邏輯、柯達官方釋出給 Windows 3.1 的舊軟體 pta31.exe,以及直接比對相機吐出來的十六進位資料。他把原本只服務 QuickTake 150 的 RADC 解碼器擴充成能同時處理 DC50 的 KDC 封裝格式,並依職責重新拆分程式碼,順便修掉幾個會讓特定影像資料使解碼器當機或崩潰的邊界錯誤。

  • 115,200 bps 高速序列傳輸,比舊款 QuickTake 150 快上許多
  • 沿用同一套序列傳輸框架,不需要客製化的原廠傳輸線
  • 完整支援縮圖預覽、日期/檔名/閃光燈/畫質設定顯示、影像下載與刪除

實際效果

成品是同一套 Apple II 軟體現在能認得兩個世代的柯達相機,DC50 的縮圖預覽、設定顯示、下載與刪除功能都完整可用,文章附上的截圖也展示了實際的選單與影像傳輸畫面。也就是說,一台 1983 年的 8 位元家用電腦,靠純軟體逆向工程吃下了一台晚了整整 13 年才問世、規格完全不同代的數位相機。程式碼(kdcpi)已公開在 Leroy 的 GitHub 上,延續他長期經營的 Apple II retrocomputing 系列文章。

原始來源:Colin Leroy 部落格colinleroy/kdcpi (GitHub)


用 S-expression 把整個 WebGPU pipeline 打包進一張 PNG 圖

hugodaniel.com · 2026-08-23

開發者 Hugo Daniel 在 2026 年 8 月 23 日發文,介紹他持續開發兩年半的專案 pngine——一套用 S-expression(類似 Lisp 的括號語法)描述 WebGPU 圖形管線的宣告式引擎。跟平常寫 WebGPU 要照順序呼叫 createShaderModule()createRenderPipeline() 等一連串 JavaScript API 不同,pngine 把整個 pipeline 寫成一份跟 WebGPU 規格「一比一對應」的資料描述檔,最後甚至能直接編譯成一張看起來像普通圖片的 PNG 檔。

原本的問題

WebGPU 的 pipeline 需要照特定順序建立多個物件——shader module、pipeline layout、render pipeline、command encoder、render pass,物件之間互相引用,順序寫錯就整個失敗,而這些設定又散落在一堆指令式(imperative)的 JS 呼叫裡,很難單獨拿出來分享或跨平台重用。Hugo Daniel 認為,要讓瀏覽器 JS、Rust wgpu、Android、iOS 等不同 runtime 共用同一份 GPU pipeline 設定,就必須先把它變成語言無關的宣告式資料格式,而不是綁死在某一種語言的 API 呼叫順序上。

採用的方法

pngine 用 SJON(S-expression 版的 JSON)描述 pipeline,每個節點對應一個 WebGPU 規格裡的物件:

(shader-module :name shader :code "...")
(render-pipeline :name pipe :layout auto ...)
(render-pass :name draw ...)
(frame :name main :perform [draw])

裸露的識別字(如 shaderpipedraw)在編譯期會被解析成交互參照,不需要手動管理 handle。整套工具鏈分兩階段:一顆用 Zig 寫的編譯器先拿 SJON 原始碼對照內建的 WebGPU schema 做離線驗證(不需要真的開一個 WebGPU context),挑出實際用到的功能模組,把 bytecode 壓成 .pngb、DEFLATE 壓縮後跟對應的 WASM 執行器一起塞進 PNG 的資料區塊;瀏覽器端只要一個約 2KB 的 loader,就能把 bytecode 與執行器從圖片裡挖出來,實例化 WASM 並接上真正的 WebGPU。

  • pngine validate --strict:離線驗證語法、語意與 WGSL,把警告當成錯誤處理
  • --minify:壓縮內嵌的 WGSL,約再省 30% 體積
  • --html:輸出 1–3KB、內含原生 WebGPU JS 的獨立 HTML,不需外部依賴
  • pngine bundle:把 shader 與素材連同 manifest 打包成 ZIP

實際效果

文章展示的三角形範例編譯後只有 505 bytes 的 bytecode,另一個帶粒子噴泉與實例化樹木效果的範例也只有 517 bytes,證明宣告式描述加上共享的 WASM 執行器,能把原本要幾十行 JavaScript 的 pipeline 壓進小到能塞進圖片 metadata 的空間。專案採用 CC0 1.0 公眾領域授權,文章也連結了作者先前寫的 WGSL 工具鏈 wgslender 與 SJON 格式介紹文,顯示這是他長期建構同一套宣告式 WebGPU 工具鏈的其中一篇。

原始來源:Hugo Daniel 部落格HugoDaniel/pngine (GitHub)


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