延遲與吞吐量(latency vs throughput)
/ LAY-tun-see versus THROO-poot /
延遲與吞吐量,是關於「快」的兩個不同問題,人們卻老是把它們搞混。延遲,是單個請求拿到答案要等多久——也就是你本人切身感受到的那份等待。吞吐量,則是系統每秒能為所有人辦完多少個請求。一家咖啡店把這事說得活靈活現:延遲是你為自己那一杯排了多久的隊;吞吐量則是這家店一小時能出多少杯。兩者不是一回事,而且改善其一,可能傷到另一個。
矛盾就在這裡。要抬高吞吐量,你往往會把許多請求攢成一批,一趟高效地處理掉——就像咖啡師一次做十二杯拿鐵,而不是一杯一杯做。這對「每小時總杯數」是好事,可第一位顧客如今得等整批都做好,於是他的延遲變糟了。往另一頭使勁——顧客一到就立刻服務——延遲妙極了,可機器在兩單之間半閒著,總吞吐量便掉了下來。
為什麼這很重要:幾乎每一個服務決定,骨子裡都是一場延遲與吞吐量的權衡,而正確答案,完全取決於用途。一個即時聊天機器人必須把延遲壓低,因為有個大活人在等;而一個通宵給十億條記錄打分的任務,只在乎吞吐量,因為沒人盯著鐘看。誠實的要點是:你通常沒法同時把兩者都頂到最高——好的工程,意味著搞清楚你的用戶真正需要哪一個,再有意地把另一個讓出去。
一次只服務一個請求:每個用戶等 50 毫秒(延遲很棒),但 GPU 每秒只能處理 20 個請求。把 32 個攢成一批:每個用戶如今得等 200 毫秒(延遲變差),可 GPU 每秒能處理 160 個請求——吞吐量是原來的 8 倍。
同一套硬體,兩個相反的目標——攢批,是拿每個用戶的等待去換總產能。
當心平均數。一個系統平均延遲可能很漂亮,卻仍讓人覺得糟糕透頂——只要最慢的那 1% 請求要花上好幾秒,也就是所謂「長尾延遲」。對面向用戶的系統而言,最壞情況(常以 p99 來報)通常比平均值更要緊。