原始圖片、標示出的 seam 路徑(紅色)以及 content-aware 裁切後的結果

我們試著讓 Seam Carving 在 GPU 上跑更快——然後我們證明了為什麼它不能

完整論文:seam_carving.pdf · 程式碼:YuXiangLo/NTUPDP2026 與吳雅蓁、羅宇翔合作,台大平行程式設計,Spring 2026。 起點 Seam carving 是一種 content-aware 的圖像縮放演算法。它不是裁切或縮放,而是移除圖片中「最不重要」的像素路徑——seam——同時保留視覺上重要的區域。效果出乎意料地好。 演算法主要有兩個沉重的步驟:先計算 energy map(梯度大小,對整張圖掃一遍);再執行動態規劃(DP)找出從上到下代價最小的連通路徑。移除那條 seam,重複。 在 CPU 上,這對大圖來說很慢。單核心處理一張 8K 圖(7680×4320,約 3300 萬像素)每條 seam 可能要幾百毫秒。我們有 V100。Energy 計算是完美的平行問題。問題看起來很明顯:能快多少? 我們原本預期找到一個新的優化方式,拿到一個漂亮的數字。最後我們證明了為什麼漂亮的數字在結構上不可能達到——而這才是更有趣的結果。 DP 的問題 用 Nsight Compute 對 1428×968 的圖做 profiling,問題立刻清楚了:DP kernel 佔每條 seam 牆上時間的 ~93%。其他全是雜訊。 DP 的遞推關係是: 1 M[i][j] = e[i][j] + min(M[i-1][j-1], M[i-1][j], M[i-1][j+1]) 每一行依賴上一行。全部 H 行都是串行的。每行內部的 W 列可以平行——但在第 i-1 行完成之前無法開始第 i 行,沒有任何重新排序能消除這個依賴。這就是我們一直碰到的牆。 優化路徑 我們在每個步驟以 Nsight 為導引,建立了五個逐步改良的 kernel。 Naive 每行啟動一個 CUDA kernel。在 4K(H=2160)時,每條 seam 要啟動 2159 次 kernel,每次都有完整的 global memory 往返。小圖上 overhead 主導,大圖上 global memory 流量主導。 ...

June 14, 2026 · 3 分鐘 · Yi-Wei Lien
ShopBack 離職禮物——記事本、咖啡、襪子和個人卡片

在 ShopBack 的六個月:後端實習的尾聲

昨天是我在 ShopBack 的特別一天——Town Hall 日,六個月旅程的句點。帶著滿滿的感謝、回憶,和對接下來的期待。 在後端核心體驗團隊擔任軟體工程師實習生的這半年,我有機會參與各種有影響力的專案——從客服和 Watchlist Service,到 Notification Service,最後是 Travel 專案。能夠在高流量的真實系統中貢獻,留下第一個工程師的實際足跡,是非常難得的經歷。 剛開始的時候,我一邊摸索一邊學習,有時候會卡關,但一直都在進步,一直都很享受這個過程。即使只是觸及了大規模系統設計的皮毛,我很慶幸能成為這個充滿熱情、樂於合作的團隊的一部分。 透過這些經歷,我也深化了解決問題的方式——尤其是善用 AI 工具,做到從分析、Spec Review,到實作和報告的端到端流程。 感謝我的主管 George Lee,他的智慧和對「Work Smarter」的示範讓我獲益良多。也感謝 Shih-Yuan Chen 和林一豪在客服服務的帶領,Hao-Ping Shih 在 Notification Service 的支持,以及 Nick Chen 和 Tim Pei 在 Travel 專案的指導——還有所有在這段旅程中給予幫助和協作的人。 在 ShopBack 工作既鼓舞人心又令人興奮——置身於一個快速成長的公司和鼓勵主人翁精神與好奇心的文化中。 這一章結束了,但所有的學習、友誼和能量,都會帶著往下走。 向前,繼續前行。

November 20, 2025 · 1 分鐘 · Yi-Wei Lien
TypeScript monorepo 架構圖——微服務共享單一 repo

整個團隊都沒做過的那次遷移

背景 Shopback 的後端跑在一組微服務上——每個服務各自管理一個領域,透過網路溝通,各自擴縮容。這個架構有清楚的好處,也有顯而易見的成本:閒置的 container 仍然佔用分配的資源,就算當下沒在處理任何流量,機器費照算。 遷移到 monorepo 架構的動機之一就在這裡。TypeScript monorepo 讓多個服務可以共享同一個 process 和資源池,閒置的開銷縮小,機器費也跟著縮小。 我的子任務很具體:兩個服務原本透過網路互相呼叫 API,遷移之後改為在 monorepo 裡直接呼叫函式。聽起來是個重構。實際做起來,變成我實習期間最困難的一件事。 為什麼這件事很難 第一層困難是技術面。Shopback 的 TypeScript monorepo 結構主要是為 library 和共用套件設計的——這是 monorepo 最常見的使用情境。在這個結構裡跑真正的後端服務是另一回事。服務有自己的啟動生命週期、自己的 framework 相依、自己的 runtime 問題。Library 沒有這些。 團隊裡已經有工程師實作了部分 monorepo,但針對的是 library 使用情境。他們的程式碼我看得到,但沒辦法直接套用——他們設計時的假設對 running services 不成立,尤其是不同後端 framework(我們跨服務用了不只一種)在 monorepo 的依賴解析機制下如何互動。 還有版本相容性的問題。TypeScript monorepo 在 workspace 層級管理套件版本,而某些跨服務依賴有寫死的版本假設,合在一起跑就會出現衝突。這不是能用 grep 找到的 bug,而是跑起來才會炸、而且 stack trace 看不出所以然的那種問題。 Mentor 也沒有答案 第二層困難是:我的 mentor 也沒做過這件事。 這不是批評,就是事實。他有自己的工作,能幫的地方他都幫了。但針對我在解的這個遷移問題,他跟我一樣是從零開始。 所以我在一個技術問題上獨立工作,沒有團隊裡的前例,codebase 夠大、搞清楚相關部分要花真正的時間,而 framework 設定在內部也缺乏文件記錄。 兩三個 Sprint,我進度緩慢。我能看出需要做什麼,但真的讓實作跑起來是另一回事。 那條 Slack 訊息 最後我做了一件我一直猶豫要不要做的事:主動傳訊息給另一個團隊的資深工程師,他是我知道曾碰過 codebase 相鄰部分的人。 ...

November 1, 2025 · 1 分鐘 · Yi-Wei Lien
Shopback app 選擇面板 — 列表中的商家圖示

讓我重新理解「有影響力」的那個小功能

在 Shopback 有一個功能,我當時幾乎沒多想。 需求很單純:PM 有一個 app 內選擇面板的設計——一個讓用戶從列表中選取項目的小 UI 元件。設計稿上要在每個項目旁加上商家圖示。我的部分是跟前端團隊協作,確保後端整合正確接上。沒有什麼技術難度,純粹是協調工作:PM 有想法,設計有 spec,我幫忙把兩端串起來讓功能可以上線。 就這樣。 上線後,stakeholder 分享了數據。那個面板的進入率——看到頁面後實際點進選擇面板的用戶比例——從大約 10% 跳到 40% 以上,提升超過 30%。 我記得看到這個數字的時候,停頓了一下。 我原本隱隱持有的模型 在這之前,我對軟體工作的影響力有一個安靜的假設:影響力跟難度成正比。問題越難,就越重要。系統遷移、效能重構、不直觀的演算法——那才是「真正的工作」。UI 調整?清單上加幾個圖示?那幾乎算不上工程。 但數據不同意。 那個面板的改動影響了大量用戶的操作。它改變了人們打開 app 之後做的事。而我花了更多時間做的那些工作——跨服務 API 遷移、錯誤率降低——固然重要,但它們對用戶的可見影響是比較安靜的。基礎設施工作通常都是如此。 圖示是半天的協調工作,換來 30% 的行為轉變。 為什麼好設計是真正的工作 圖示真正做到的事,是給面板裡每個項目一個身份。加圖示之前,列表項目大概只是文字或通用條目。加了之後,每一行都有視覺錨點——一個在說「這是一個真實的東西、一個可辨識的東西、值得你注意的東西」的信號。用戶對這個信號做出了回應。 這不是新觀念。但「知道設計很重要」和「看著設計在一個你指得出來的指標上發揮作用」,是兩件不同的事。 我更新的東西是「工程貢獻」的範圍。讓後端整合正確接上,讓圖示在對的時間載入,又不破壞面板原本的行為——這是真實的貢獻。不性感,但它是讓 PM 願景可以上線的那個東西,而能上線的東西是推動數字的那個東西。 關於驕傲這件事 有一件事我沒有完全預期到:看到數字的時候,我的感受是什麼。 那種驕傲不是來自技術難度,而是來自效果。我碰過的東西改變了大量用戶打開 app 之後的行為。這和乾淨解決一個困難問題的滿足感不太一樣——沒那麼智識性,但更連接到真實的人。 我覺得這是軟體業值得追求的那種驕傲形式。不是「我解決了一件很難的事」,而是「我做的東西現在已經是某些人移動世界方式的一部分了」。即使那個移動很小——多點了一個按鈕,看到了之前沒看到的東西。 有影響力的工作不一定是房間裡最難的問題。有時候,它看起來就像一排圖示。

October 10, 2025 · 1 分鐘 · Yi-Wei Lien
Bubblo — 2D 像素平台遊戲標題畫面

一學期帶一個遊戲專案:敏捷沒有教你的事

背景 2025 年 4 月。六個人,一個學期,目標:用 Unity 完成一款完整的 2D 平台遊戲。 這款遊戲叫 Bubblo。你扮演一隻泡泡生物,在像素風格的奇幻樂園中探索,拯救被關在籠子裡的村民,同時對抗各種針型敵人——蜜蜂、跳躍蜘蛛、還有一隻非常想把你戳破的獨角獸。核心機制是泡泡物理:在關卡中彈跳漂浮,用你的柔軟特性同時解決移動和戰鬥問題。 我是專案負責人。我的工作是把一堆模糊的想法轉化為真正可以交付的遊戲細節——然後讓另外五個人在三個月內朝同一個方向前進,而且沒有人中途放棄。 我從 Cmoney 帶來了什麼 在這之前幾個月,我剛結束在 Cmoney 的後端實習,九個月的真實敏捷流程:衝刺規劃、每日站立、Sprint Review、回顧。我參加了夠多這些會議,對它們的用途有個淺層的理解。 所以開始 Bubblo 時,我有一套框架。我們會跑兩週的 Sprint,有正式的任務看板,做 Retrospective。我知道這些詞彙。 但我還不完全理解的是:這些詞彙背後有一套隱含的假設——關於人們如何安排時間、「可用性」代表什麼,以及一個團隊最主要的義務是什麼。 在公司,每個人最主要的工作就是這個專案。Sprint 是容器。敏捷儀式有效,是因為它設計的前提是全職投入。 在學校,這對任何人都不成立。 為什麼儀式不管用 我們的團隊成員有實驗室工作、其他課程、課外活動、實習申請要處理。有人在 Sprint 中途趕一篇研究論文;有人有兼職工作。每個人都是一個完整的人,在 Bubblo 之外有完整的生活。 要跑一場完整的 Sprint 規劃,意味著請人們拿出他們根本沒有的兩個小時。開回顧會議感覺很表演,因為真正的阻礙只是大家很忙、而且對此感到愧疚。每日站立變成焦慮的來源,而不是同步機制。 僵硬的結構在製造摩擦,而不是消除摩擦。 設計一個真正合適的工作流程 所以我把它拆解精簡。 任務看板留下來——這是最有價值的東西。所有人都能非同步看到「目前有什麼任務、什麼在進行中、什麼被卡住了」。維護這個看板不需要任何儀式,成本幾乎是零。 我用非同步確認取代了排程站立:每次工作時段開始時發一條短訊息,沒有格式要求,只是「今天在做 X,如果 Y 沒解決可能會卡住」。低壓力,大家真的會看。 每週同步變成一場有單一議程的會議:現在誰被什麼卡住了,我能在接下來三十分鐘內幫你解決嗎? 不是進度報告,不是狀態更新。只有阻礙。跑二十到三十分鐘,阻礙清完就結束。 背後的原則是:尊重每個人的第一義務不是這個專案,但讓人們在有時間的時候容易貢獻。精簡,但不鬆散。 沒有人想要的 OOP 重構(但我們還是做了) 大約六週後,我們遇到了問題。角色行為的程式碼已經有機生長成一團亂麻。多個人在碰同一塊,一個地方的改動會在另一個地方產生難以追蹤的 bug。 我們重構了。Sprint 進行中。學生時程上。 在任何遊戲程式碼開始之前,我已經在紙上把元件架構設計成圍繞 Observer、State Machine 和 Command 模式。重構是要在實作中真正落地這些模式:讓狀態轉換變得明確、讓事件系統成為跨元件溝通的唯一真相來源。 這是正確的決定。重構之後,新增一種敵人類型或玩家狀態變成了一個有邊界的操作。Sprint 最後三分之一明顯比前三分之二不混亂。 時機感覺不對,但繼續修補一個纏繞的 codebase 會更糟。有些技術債複利速度夠快,必須提早還清。 AI 工具的時代背景 整個專案我們用 ChatGPT。那時候還沒有 IDE 代理工具——沒有整合的程式碼生成,頂多就是補全。主要用法是:描述一個問題,拿到一個草稿,再把草稿改成能用的東西。 ...

July 10, 2025 · 1 分鐘 · Yi-Wei Lien
我的 Cmoney 員工識別證——連奕維,工程部·後端組

我在 Cmoney 的第一份實習——也可能是最後一個「真正手寫程式碼」的年代

第一份的結束 4 月 20 日。在 Cmoney 擔任後端開發工程師實習生九個月後,最後一天。 第一份實習很奇特。你不知道自己不知道什麼,所以你無法分辨哪些是正常的,哪些是這個地方特有的。現在回頭看,我覺得自己在最關鍵的地方運氣很好:我落腳在一個有真實程式碼、真實規模,以及一個把教導這件事認真看待的導師的地方。 Cmoney 是什麼樣的地方 Cmoney 是台灣最大的金融資訊平台——700 萬 App 下載量,股市資料、投資組合追蹤和散戶投資人廣泛使用的金融分析工具的核心基礎設施。這裡的後端不是新創公司的乾淨程式碼。它是十年的生產歷史、累積的決策和繼承下來的限制。在這裡工作,你會感受到那份重量。 我在核心後端積極進行微服務重構期間加入。目標是把業務邏輯從單體架構中抽出來,放進有版本管理的服務合約。我的工作是為旗艦產品的九個後端 API 實作這些重構。 用艱難的方式學 OOP 我從課本學過物件導向程式設計。Cmoney 教了我 OOP 實際上能帶來什麼。 我繼承的程式碼沒有清楚的關注點分離。業務邏輯直接在 Controller 裡。資料存取散落各處。狀態在層次之間洩漏。測試全綠——程式碼無法維護。 重構它意味著在改善之前先理解它為什麼這樣寫。而要理解這件事,我首先需要搞懂 ASP.NET Core、C# 的型別系統,以及本該存在但不在的那些模式。 Tom——我的導師——在這件事上展現了我後來才完全體會的耐心。他沒有只告訴我要改什麼。他解釋為什麼原本的設計會造成問題,原本的模式應該是什麼,以及如何在不影響另一端 700 萬用戶的情況下逐步遷移過去。這是一種和懂得那些模式不同的能力。它是知道這些模式在什麼時候重要。 到了第三個月,我已經從問「這裡我該怎麼做」,變成問「這段程式碼假設了什麼,這個假設現在還成立嗎」。這個轉變才是真正的收穫。 規模下的金融 API 這個領域有它自己的挑戰。金融資料在開盤時不是一般流量。數百萬用戶同時刷新投資組合、執行選股器、查看即時股價。只有在那個規模和那個時間窗口才會出現的並發邊緣案例。你很快就學到「測試時可以跑」和「真實負載下可以跑」是兩種不同的主張。 我深入熟悉了 C# 的高並發模式——async/await、連線池、時效性資料的快取策略——因為替代方案是在交易日早上九點發生事故。 最後一個「真正手寫程式碼」的年代 我在 2025 年 4 月離開 Cmoney。兩三個月後,產業開始快速移動。 Agentic Programming——AI Agent 從頭到尾處理從 Spec 到程式碼到測試的完整實作——從 Demo 變成了真實工程師使用的真實工作流程。「初階開發工作」的樣貌開始即時改變。 我還不確定這對早期職涯工程師意味著什麼。但我知道,那九個月手動繼承破損的生產程式碼、在早上九點追著 Bug 穿越三個服務層、和導師爭論這個抽象層到底值不值得那個間接性——這類學習不會被 Agent 抽象掉。理解程式碼在做什麼,以及設計是否合理,仍然是難的那個部分。 也許改變的是,機械性的部分變快了。判斷力這個部分沒有。 感謝 感謝 Tom,用應有的方式教我 ASP.NET、C# 和 OOP——透過有真實後果的真實程式碼。感謝在每一步都解釋「為什麼」的耐心。 感謝整個團隊,以及在我到來之前流過 Cmoney 歷史的大量工作,讓我能貢獻其中一小部分。 ...

April 20, 2025 · 1 分鐘 · Yi-Wei Lien