iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
佛心分享-SideProject30

打造 APR Engineer 的生產力平台,從 Flow Tracer 到 SignOff DashBoard 的落地實戰系列 第 9

【Day 09 】 Flow Tracer 架構哲學:什麼時間點該做什麼事,自己要清楚

  • 分享至 

  • xImage
  •  

前言

做 EDA flow 最容易翻車的地方,通常不是「 job 寫錯指令」,而是把不同時間點該做的事混在一起

什麼時間點該做什麼事,自己要清楚。

這句話聽起來像人生雞湯,但在 flow runner 裡,它幾乎就是全部。Altair FlowTracer 把這件事做到極致——用 tracing 抓住「這個工具什麼時候該跑、依賴什麼檔、能不能並行」。WinFlow 走的是比較輕量、宣告式的路:把同一套時間哲學寫進 flow.json 與 Runner。今天就把這條時間軸攤開來看。

先講現場:為什麼「時間點」會變成架構問題

半導體實作流程很長。一個 place-and-route 風格的 demo,光是:

FloorPlan ───► Place ───► CTS ───► Route 
   │           │         │          │    
   ───► QC      ───► QC  ───► QC    ───► QC  

就已經有兩種完全不同的「先後」:

  1. 主流程必續等:沒有 floorplan 的結果,place 不該開始。
  2. 檢查可以並行Q_PLACE 檢查 PLACE 的產出,但不該擋住 CTS 開工。

如果系統搞不清楚這兩件事發生在哪個時間點、由誰決定,後果很具體:

  • 如果拿 stage 名稱當成執行順序 → 明明可以並行的 job 會被串成一條線
  • inputs / outputs 當成排程 → 檔案還沒寫完,下游就會以為自己 ready
  • 把「產生流程」跟「執行流程」寫在同一個函式 → GUI 一改,下一次 export 又會被演算法加回來

FlowTracer 教會我的三件事

之前有提到坊間一個有名的 FlowTracer(Altair),它的核心是三件事:

  1. 相依性是一等公民:
    一個 job 能不能跑,只看它的前依賴是否滿足
  2. 並行是被發現的,不是被 hard code 的:
    job 若彼此沒有依賴,它們就要能同時跑。系統的工作是主動把這件事揭發出來,而不是讓使用者自己去設定
  3. 失敗停在正確的時間點:
    上游掛了,下游的 job 不該被送進 queue。修完以後,從斷點繼續,而不是整條 flow 重跑。

WinFlow 沒有做 FlowTracer 那種「執行時去 trace 工具開了哪些檔」的法術。我把依賴寫在 flow.json 裡parents / children 管排程,inputs / outputs 管檔案是否真的在磁碟上。想法相同,實作更直白—— 因為目標是讓寫 flow 的人、跑 flow 的人、畫 DAG 的 GUI,對同一套規則達成共識

WinFlow 的時間軸:一張圖講完

https://ithelp.ithome.com.tw/upload/images/20260813/201279323ZUB07toXY.png

對應到 repo 裡的模組,大致是:

Winflow 系統階段權責與邊界表

核心架構原則: 每一欄都有專屬負責人(Who)與核心任務(Do),並嚴格禁止越權行為(Don't)。

時間點 誰負責 做什麼 刻意不做什麼(禁止越權)
設計時 winflow/generator/ 組出 job、command、I/O,必要時種子化 DAG bsub、不輪詢
編輯時 Generator GUI + editor/deps.py parents / children 不重新 annotate 覆蓋使用者的 unlink
載入時 FlowValidator 檢查 JSON 結構、重複 stage、relation 一致性 不看磁碟上的檔案
排程時 FlowRunner._run_dag 父節點都完成才放行;ready 的 job 並行 不管 stage 順序、不管檔名
提交時 run_job 確認 inputs 存在,再送 LSF 還沒跑完就去驗 outputs
輪詢時 LSFJobManager.wait_job bjobs 看到 DONEEXIT 用 stage 名稱猜狀態
完成時 wait_job + validator DONE 之後才驗 outputs 把「檔案在不在」當成「能不能開始」
重跑時 job_filter / GUI Rerun 已 DONE 的跳過,從失敗點繼續 把成功的 job 再送一次
同步時 Runner「Sync from Generator」 只在 runner 閒置時把編輯結果灌回去 有 job 在跑還去改 DAG

三份契約,請不要混用

參考 winflow/graph.py 開頭:

Jobs schedule solely from each job's parents / children.

Stage/task nesting remains for identity, logging, and editor tags only.

Inputs/outputs do NOT schedule jobs.

這三句話可以當成 WinFlow 的憲法。拆開來看:

1. Stage / Task:身份,不是時間

FLOOR_PLAN/PLACE/PLACE 這種 key 裡的 stage、task,是給人看的標籤:日誌、畫布分組、GUI 點選。Runner 不會因為 PLACE 寫在 FLOOR_PLAN 後面,就自動等它。

所以你可以把質檢 job 放在獨立的 stage,也可以跟主 job 放在同一個 stage——對排程沒差。差的是 parents 指到誰。

2. parents / children:什麼時候「可以開始」

這才是時間軸上的開關。_run_dag 的邏輯,非常樸素:


remaining[key] = 還沒完成的 parent 數量

ready = remaining == 0 且尚未送出

某個 job 一 DONE,就去把它的 children 的 remaining 減一。減到零,立刻丟進 thread pool。兩個都沒有互相依賴的 root(例如 ithome 裡的 IT_1IT_2_*)會一起跑——並行是 DAG 長出來的,不是額外寫的 parallel: true

3. inputs / outputs:這個時間點「檔案在不在」

檔案檢查發生在兩個非常特定的瞬間:

  • 提交前validate_paths(job_inputs, "input")

沒有 input,根本不該 bsub。這是保險,不是排程。

  • LSF 回報 DONE 之後validate_paths(job_outputs, "output")

cluster 說做完了,但約定的 .done 沒出現,這份工作仍算失敗。

很多人會下意識覺得「output 就是下一棒的 input,所以檔案依賴 = 執行順序」。WinFlow 刻意拆開:檔案告訴你產物有沒有寫出來;DAG 告訴你什麼時候允許下一棒入場。兩者常常對齊(Generator 也會用 outputs → inputs 去種子化邊),但對齊是結果,不是定義。

質檢旁掛就是最好的例子:Q_PLACE 的 input 是 PLACE 的 output,所以它晚於 PLACE;但它不是 CTS 的 parent,所以 CTS 不必等質檢。時間關係被寫在邊上,而不是寫在檔名裡。

一個 job 自己的一生

把鏡頭從整條 flow 拉近到單個 job,run_job 的順序幾乎是照抄整套做法的:


SKIP?(Rerun 時已完成)

→ 通知 GUI:pending

→ 驗 inputs

→ bsub

→ 通知 GUI:PEND

→ 輪詢 bjobs

→ DONE:驗 outputs → 通知 job_done

EXIT / 缺檔:通知 job_failed,丟例外

注意幾個「不要提早做」:

  • 還沒送進 LSF,不會去輪詢
  • 還沒 DONE,不會去驗 output(避免把「正在寫檔」當成失敗)
  • 失敗發生在缺 input、submit 失敗、或 wait 期間,一律走 job_failed

這樣 GUI 不會停在一個永遠的 RUN

GUI 也守同一個時鐘。Runner 還在跑時,Sync from Generator 是關掉的——你不會一邊執行舊 DAG、一邊把新 DAG 灌進去。Stop 走 bkill 與重試;Reset Flow 清的是本地狀態與 log,不會偷偷幫你殺 cluster 上的作業。每個按鈕都對應一個時間點,而不是「反正都能按」。

用 ithome DAG 當一次演習

內建的 ithome flow 長這樣:


IT_2_0 .. IT_2_N ────┐
                     v
IT_1 ──> IT_3 ──> IT_4 ──> IT_5
           │                ^
           └────────────────┘

架構核心概念: 系統排程嚴格依賴 DAG 的 parents / children 關係,檔案的存在是提交前(Inputs Verification)的保險,而非放行排程的依據。

問題 答案與執行邏輯
IT_1IT_2_* 誰先跑? 兩者都是 root(parents 為空),同時可以 ready
IT_4 什麼時候能送? IT_3 以及所有 IT_2_* DONE
IT_5 能不能關掉? 設計時: yaml 的 USE_IT_5執行時: DAG 裡根本沒這個 node
IT_3 的 output 同時餵給 IT_4IT_5,會不會變成排程邊? 檔案是保險;真正放行的仍是 parents

如果你發現自己在用「IT_4 寫在 JSON 比較後面」來解釋執行順序,就代表時間點又混了。JSON 的排列是給人讀的;Runner 只認邊。

總結

  • Generator 沒有 LSF client
  • Runner 不負責發明新的依賴
  • 設計時組流程,執行時只消費流程
  • 設定分成三層,也是時間點:站點預設(config.json)→ 這次設計(setting.sh)→ 這次固化的流程(flow.json
  • 排程只看 parents / children
  • inputs 在提交前驗,outputs 在 DONE 後驗
  • stage / task 是名牌,不是日曆
  • 失敗停在正確的節點,重跑就該從那裡繼續

FlowTracer 用 tracing 在執行中抓住事實;WinFlow 用宣告在執行前把事實寫下來。
這是兩種實作,但是是同一種紀律 —— 流程工具如果自己都搞不清楚時鐘,使用者更不可能搞得清楚。


上一篇
【Day 08 】 Flow Generator 靈魂擴充:透過範本與設定檔實現自動化生成
下一篇
【Day 10】 Flow 可視化(Visualization):畫出連菜鳥都能一眼看懂的流程圖
系列文
打造 APR Engineer 的生產力平台,從 Flow Tracer 到 SignOff DashBoard 的落地實戰13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言