iT邦幫忙

2026 iThome 鐵人賽

DAY 26
0
ChatGPT & Codex

ChatGPT + Codex 打造高效能 AI 開發工作流系列 第 26 篇

Day 26: 系統效能調優:利用 ChatGPT 快速發現 Memory Leak 與 I/O 瓶頸

  • 分享至 

  • xImage
  •  

Day 26: 系統效能調優:利用 ChatGPT 快速發現 Memory Leak 與 I/O 瓶頸 (Finding Memory Leaks and I/O Bottlenecks)

本日核心價值 (Core Focus): 把 CPU、RSS、p95 latency、slow query 與一小段熱點程式碼組成「可驗證假設清單」,讓 ChatGPT 協助縮小 Memory Leak 與 I/O 瓶頸範圍;AI 負責提出量測計畫,profiler 與 EXPLAIN ANALYZE 負責給證據。

概念說明與實戰情境 (Overview)
線上服務變慢時,最容易失敗的做法是把整支 service 貼進 ChatGPT,請它「找 Memory Leak」。沒有 RSS 曲線、p95 latency、GC 次數或 slow query,模型只能依常見反模式猜答案,接著建議一次大重寫。正確工作流相反:先固定觀測窗口與指標,再截熱點函式(通常 30–80 行),要求模型產出假設、對應證據、以及「還沒量到之前禁止改架構」。ChatGPT 擅長把指標異常對齊到 IDisposable、未關閉檔案、無界快取、N+1 Query 這類模式;它不能取代 dotMemory、py-spy 或資料庫的 EXPLAIN ANALYZE。

關鍵操作與範例 (Implementation & Example)

把效能調優拆成四步,每一步都要留下數字,而不是感覺。

  1. 固定觀測窗口: 例如「過去 15 分鐘、同一個 pod / 同一個 API 路徑」。
  2. 收集最小指標組: CPU、RSS、p95 latency、error rate、slow query 次數;缺一項就先補觀測,不要開始改碼。
  3. 截熱點,不要貼整 repo: 堆疊取樣前 3 名、或 APM 裡耗時最高的函式。
  4. 要假設清單,不要要重寫: 每條假設必須寫「若成立會看到什麼數字」。

最小指標組可直接當 Prompt 的輸入表:

指標 為什麼要量 常見對應方向
CPU 區分計算熱點與等待 I/O 忙迴圈、序列化、壓縮
RSS / Working Set 區分成長型 leak 與短暫峰值 未釋放、無界快取、大物件
p95 / p99 latency 使用者體感,不是平均值 lock、同步 I/O、下游逾時
Slow query / DB time 把應用層與資料庫切開 缺索引、N+1、全表掃描
GC / allocations(若有) 驗證「看起來像 leak」是否只是分配過多 短命物件、boxing、大 List

以下 Prompt 強制模型先要證據。可換成團隊現用的 ChatGPT API 模型;重點是輸出契約,不是模型品牌。

你是效能除錯助理,不是重構機器人。

輸入:
- 觀測窗口:{window}
- 指標:CPU={cpu} RSS={rss_trend} p95={p95} slow_query={slow_sql}
- 熱點程式碼(僅此一段,禁止假設其他檔案存在):
{code}

規則:
1. 先列出 3–5 條假設。每條必須包含:可能原因、會影響的指標、下一步要量什麼、如何否證。
2. 若指標不足以支持「Memory Leak」這個詞,必須改寫成「尚未證實的記憶體成長」,並說明缺哪筆證據。
3. 在沒有 profiler / EXPLAIN ANALYZE / 複現步驟前,禁止建議重寫模組、換 ORM、或導入新快取框架。
4. 若程式碼出現 IDisposable、HttpClient、檔案 handle、靜態 List/Dictionary,只能標成「高優先驗證點」,不能直接當結論。
5. 輸出 JSON:hypotheses[], required_measurements[], safe_next_command[]。

C# 常見的假陽性是「看到 new HttpClient() 就判定 leak」。HttpClient 實作 IDisposable,但每個 request 都 using 掉,容易造成 socket 耗盡;完全不釋放又會讓連線與 handler 累積。正確方向是 IHttpClientFactory(或受控的長生命週期實例),而不是「全部加 using」或「全部不要 Dispose」。

// 反模式 A:每個 request new HttpClient,且不釋放
public async Task<string> FetchAsync(string url)
{
    var client = new HttpClient(); // handler / socket 可能累積
    return await client.GetStringAsync(url);
}

// 反模式 B:每個 request using HttpClient —— 看起來很負責,卻可能打爆 sockets
public async Task<string> FetchWrongDisposeAsync(string url)
{
    using var client = new HttpClient();
    return await client.GetStringAsync(url);
}

// 正途:由 factory 管理 handler 生命週期
public sealed class PriceClient
{
    private readonly HttpClient _http;
    public PriceClient(HttpClient http) => _http = http;

    public Task<string> FetchAsync(string url) => _http.GetStringAsync(url);
}

// Program.cs
builder.Services.AddHttpClient<PriceClient>(c =>
{
    c.Timeout = TimeSpan.FromSeconds(5);
});

把上面兩段反模式連同「RSS 30 分鐘單調上升、p95 從 180ms 到 1.2s、TCP ephemeral ports 接近耗盡」一起丟進 Prompt,模型才有資格把假設從「可能有 leak」收斂成「先查 HttpClient 生命週期與 socket 狀態」。若只有程式碼、沒有 RSS 與 port 數,答案必須停在「待量測」。

I/O 瓶頸另一個高頻模式是 N+1。應用層迴圈看起來很短,資料庫卻被打成數百次 round-trip,p95 與 DB CPU 會同時變差,但 RSS 不一定上升。這時 ChatGPT 的價值是指出「這是 I/O 放大,不是 Memory Leak」,並要求 EXPLAIN ANALYZE 與一次查詢的 round-trip 計數。

// N+1:每個訂單再查一次顧客
var orders = await db.Orders.Where(o => o.Day == today).ToListAsync();
foreach (var order in orders)
{
    order.Customer = await db.Customers.FindAsync(order.CustomerId);
}

// 一次取出:把 round-trip 從 N+1 收成 1
var ordersWithCustomer = await db.Orders
    .Where(o => o.Day == today)
    .Include(o => o.Customer)
    .ToListAsync();

對應的 PostgreSQL 驗證不是「覺得比較快」,而是:

EXPLAIN (ANALYZE, BUFFERS)
SELECT o.*, c.*
FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.day = DATE '2026-08-17';

Python 服務同樣適用同一套工作流。無界記憶體快取(靜態 list / dict 只進不出)會讓 RSS 隨流量單調上升;未關閉的檔案 handle 則較常先打中 OSError: Too many open files。py-spy 看 CPU 熱點,tracemalloc 看分配,作業系統則用開啟檔案數驗證 handle。模型仍然只負責假設,不負責宣布「已修好」。若 RSS 只在尖峰跳一下又回到基線,那是短暫分配而非 leak,Prompt 應要求先看斜率是否單調,再決定要不要提 IDisposable 或快取。把「成長持續多久、是否隨 request 數線性增加」寫進假設的否證條件,可避免把 GC 前的峰值誤判成必須重寫。

建議的驗證迴圈:

步驟 人做的事 ChatGPT 做的事
Baseline 記錄 RSS、p95、slow query 禁止給出重寫
Hypothesis 提供 30–80 行熱點 產出可否證假設
Measure dotMemory / py-spy / EXPLAIN 解讀數字是否支持假設
Change 只改被證據支持的那一段 產出最小 diff 與回歸測試
Compare 同一觀測窗口再量一次 判定假設成立或撤銷

注意事項與常見失敗 (Pitfalls)

  • 用 ChatGPT 取代 profiler: 模型在沒有 heap dump、allocation flame graph 或 EXPLAIN ANALYZE 時,仍會講得像已定位。修法:Prompt 寫死「沒有 required_measurements 的結果,不得使用 leak / bottleneck 當結論」;dotMemory、PerfView、py-spy、dotnet-counters、PostgreSQL pg_stat_statements 仍是主工具。
  • 把 using HttpClient 當萬靈丹: 這會把 Memory Leak 故事轉成 socket exhaustion,p95 更差。修法:量 HttpClient 實例數、TIME_WAIT、DNS 變更是否被長連線快取;.NET 用 IHttpClientFactory。
  • 無界快取被模型建議「再加一層 Redis」: RSS 上升的根因若是程序內 static Dictionary 只進不出,先加 eviction / size cap,不要先擴充基礎設施。修法:先證明 hit rate、entry count、過期策略,再談外部快取。
  • N+1 被誤判成「CPU 不夠」: 平均 CPU 可能很低,p95 卻很高。修法:同時看 DB time、query count per request、應用 CPU;三者對不上就禁止建議加機器。
  • 改完沒有同一窗口的對照: 沒有 baseline 的「感覺比較快」不能過關。修法:固定路徑、固定負載、固定時間窗,對照 RSS 斜率與 p95,而不是單次請求。

本日總結 (Takeaways)

  • 效能 Prompt 的輸入是指標加短熱點,不是整個 repo。
  • 先要假設與量測指令,禁止在無證據時重寫。
  • HttpClient / IDisposable、檔案 handle、無界快取、N+1 是高優先驗證點,不是自動結論。
  • ChatGPT 加速「該量什麼」;dotMemory、py-spy、EXPLAIN ANALYZE 才給「是不是真的」。
  • 每次改動只對準被數字支持的一條假設,並用同一觀測窗口回歸。

明日預告 (Next)
明天把同樣的「模組化 Prompt」用在專案起步:自動化開發環境搭建 (Dev Environment Setup) 的 Prompt 模組庫,一次產出可跑的 docker-compose、.env.example 與 Codex 用的 AGENTS.md。


上一篇
Day 25: 自動化排程工作流:結合 Make / n8n 與 OpenAI API 實現日常營運自動化
下一篇
Day 27: 自動化開發環境搭建 (Dev Environment Setup) 的 Prompt 模組庫
系列文
ChatGPT + Codex 打造高效能 AI 開發工作流 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言