為什麼「剩餘1秒」遲遲不結束?── 進度列與剩餘時間的運作原理

· 更新日期: · · Windows, 進度列, 剩餘時間, UI, 效能

看著「剩餘1秒」,已經過了30秒。以為終於快結束了,顯示卻又變成「剩餘2分鐘」。

複製檔案、安裝應用程式、匯出影片時,進度列很有用,但有時它彷彿和時鐘活在不同的世界裡。

事實上,進度列不是時鐘。進度百分比表示「已經完成多少」,剩餘時間預測「接下來大概要多久」,完成狀態則表示「必要的處理是否成功」。 把這三件事分開,就能換個角度理解「明明99%了,怎麼還沒結束」。

本文參考Windows API與UI文件,說明一般原理,並不是對某個版本檔案總管內部演算法的逆向分析。文中的數值範例和比較示範都是用來說明的虛擬工作,不是電腦或網路的實測結果。

1. 先弄清楚:究竟是什麼的100%?

假設要審閱100份文件。前99份都是簡短便條,最後一份卻是厚厚的合約,那麼「已完成99份」可以完全正確,但不代表「所需時間也已經過了99%」。

進度顯示也是如此:分母不同,意義就不同。

衡量依據 50%表示什麼 光看這個數字無法知道什麼
檔案數量 已處理目標檔案的一半 剩餘檔案的大小與處理時間
資料量 已處理目標位元組數的一半 後續速度,以及傳輸以外的階段
階段權重 已完成預設權重的一半 權重是否符合這次的實際耗時

例如,Windows的CopyFileEx使用的進度回呼會提供檔案總位元組數與已傳輸位元組數。這些是工作量資訊,並不直接提供未來還需要多少秒。1

進度百分比、剩餘時間與完成狀態的差異區分依已完成量計算的比例、基於假定速度預測的剩餘時間,以及由成功結果確認的完成狀態。已完成量與總量進度百分比剩餘量與預測速度預估剩餘時間必要的處理成功整個工作完成

圖1:比例、預測與結果,並不是同一資訊的三種說法。

本文把可測量工作量的完成比例稱為進度百分比,顯示它的長條控制項稱為進度列,對還需多少秒的預測稱為剩餘時間估算。無法提供可靠預測,不代表已經量到的進度也變得未知。

圖中實線表示始終成立的關係,虛線表示附帶條件的關係(成立條件寫在詳細頁面中各關係的說明中)。關係的完整清單(共 9 條,附依據與可信度)以及主要概念的定義,彙整在知識地圖詳細頁面(日文)。資料:JSON-LD / Turtle

2. 「剩餘10秒」如何變成「剩餘79秒」?

最簡單的剩餘時間估算公式如下:

剩餘時間 ≈ 剩餘工作量 ÷ 對未來處理速度的估計

困難的不是除法,而是未來的速度還沒有被觀測到。因此,我們用過去的速度做預測。Microsoft的Raymond Chen在2004年解釋檔案複製時間估算時,也談過預測未來的困難。那篇文章不是目前Windows所用計算公式的規格說明。2

設想一個總量為1,000 MiB的虛擬複製工作。1 MiB等於1,048,576位元組。假設工作先以80 MiB/s的固定速度執行2.5秒,接著1秒的速度降到10 MiB/s。

觀測時點 已傳輸 剩餘 用於估算的速度 剩餘時間
開始後2.5秒 200 MiB 800 MiB 80 MiB/s 800 ÷ 80 = 10秒
開始後3.5秒 210 MiB 790 MiB 10 MiB/s 790 ÷ 10 = 79秒

在這一秒裡,工作確實往前推進了。然而,完成剩餘工作的預測速度變成原來的八分之一,因此預估耗時反而增加。這個範例直接採用最近一個觀測區間的速度。

工作在推進,剩餘時間為何還會增加如果預測速度下降的影響超過剩餘工作量減少的影響,計算出的剩餘時間就會增加。1秒內完成10 MiB剩餘量減少預測速度大幅下降剩餘時間可能增加

圖2:預估剩餘時間增加,與工作實際倒退是兩回事。

「剩餘1秒」也是同樣的道理。依剛才的速度只需1秒的工作量,如果接下來的處理較慢,就無法在1秒內結束。觀測間隔和秒數的取整方式也會影響顯示。不過,數字始終不變時,也不能直接認定為正常;還需要依後文的方法,區分處理階段、畫面更新與實際停滯。

3. 取平均值就能算準嗎?

如果把每次短暫的速度變化都反映出來,數字就會上下跳動。反過來,如果只採用從開始到現在的平均速度,最初很快的那一段就會持續影響結果,讓預測遲遲無法適應後來的持續減速。

從觀測值的組合方式就能理解這種差異。假設把過去的速度80與最新速度10,各以一半權重混合,得到的預測速度是45。與只使用最新值10相比,預測會平順一些;但如果實際速度一直維持在10,它就會在一段時間內過於樂觀。顯示平順與及時跟上變化,是不同的目標。

平滑速度估計時的取捨提高最新觀測值的權重會讓估計更敏感但也更容易波動,提高歷史值的權重則較平順但跟隨變化較慢。速度觀測值提高最新值的權重提高歷史值的權重反應敏感但容易波動顯示平順但跟隨較慢

圖3:數字看起來穩定,不等於預測準確。

這不是某個產品的實作說明,而是協助思考預測方法的例子。本文的設計建議是:剛開始或剛切換階段時,不要勉強顯示秒數;有足夠觀測後,再以「大約1分鐘」等形式提供估計。與其維持沒有依據的「剩餘1秒」,不如明確說明正在重新估算,減少誤解。

4. 已完成99個檔案,資料量卻只完成9.9%

這次問題不在速度,而在計數單位。假設有100個檔案,前99個各為1 MiB,最後一個為901 MiB,總量是1,000 MiB。

前99個檔案完成時,依數量計算是99 ÷ 100 = 99%,依資料量計算卻是99 ÷ 1,000 = 9.9%。雖然只剩一個檔案,仍有90.1%的資料尚未傳輸。兩種計算都正確,只是衡量的對象不同。

最後一個檔案特別大時的進度處理完99個小檔案後,如果還剩一個大檔案,依檔案數量與位元組數計算的進度就會相差很大。99個小檔案依數量看幾乎完成最後還剩一個大檔案仍有大量資料未傳輸同一工作的不同顯示

圖4:依檔案數量達到99%,並不保證只剩1%的時間。

那麼,只看位元組數就夠了嗎?也不是。透過SMB傳輸大量小檔案時,會反覆產生建立檔案與要求往返的額外負擔。即使總位元組數與一個大檔案相同,耗時也不一定相同。3

所以,「還剩500 MiB」可以是準確的測量結果,但這500 MiB由什麼組成,仍會影響所需時間。同時顯示檔案數量與位元組數的價值,不是其中一個數字有錯,而是它們能補足彼此未呈現的資訊。

5. 「準備中」很久,有時是在尋找分母

計算百分比需要作為分母的總量。但剛下達「處理整個資料夾」的指令時,程式不一定已經列舉完裡面所有層級的檔案。

假設程式認為共有100項,處理了80項後又發現100項。同樣的80項就從80/100變成80/200。顯示從80%下降到40%,但已完成的工作沒有消失。把尚未確定的總量當作確定值呈現,才造成了與讀者預期的落差。

總量尚未確定時如何顯示進度尋找處理對象時不顯示確定的比例,等總量已知後,再搭配已完成量計算百分比。還沒有已知尋找處理對象總量已知了嗎顯示階段與已發現數量顯示百分比

圖5:沒有分母,與進度為0%是不同的狀態。

Windows的進度控制項也區分確定與不確定進度;後者表示仍在處理中,而不提供確定數值。4 對於這裡的情況,本文建議在列舉時顯示「正在尋找對象:已發現1,200項」,等總量確定後再顯示比例。沒有剩餘秒數,本身並不能證明程式什麼都沒做。

6. 「傳輸100%」與「全部結束」不是同一個邊界

6.1 最後可能還有別的工作

為了說明問題,把一個應用程式的工作分成「傳輸 → 驗證 → 確定最終結果」三個階段。如果設計要求傳輸後驗證內容,那麼傳輸結束時,整個工作還沒完成。這只是一個應用程式設計範例,不代表所有複製或安裝都遵循這個順序。

傳輸完成與整個工作完成這個虛擬應用程式在傳輸後還需要驗證並確定最終結果,因此不能僅憑已傳輸位元組數判斷整體成功。傳輸驗證確定最終結果整體成功傳輸100%只到這裡

圖6:明確說明完成的範圍,就能解釋為何100%後仍有其他階段。

此時,與其把代表整個工作的進度列固定在99%,一直顯示「剩餘1秒」,不如把狀態切換為「傳輸完成,正在驗證」。Microsoft的桌面UI指南也要求,不要在實際作業結束之前顯示整個工作已完成。5

6.2 寫入也不只有一個完成邊界

Windows通常會對檔案寫入使用快取。應用程式發出寫入與資料落到儲存媒體,具體邊界會因設定及API約定而異。6 FlushFileBuffers是把指定檔案的緩衝資訊傳送到裝置的API。7

緩衝寫入的概念圖使用緩衝時,應用程式提交的寫入與儲存媒體側的實際寫入,不應視為同一時刻發生的事件。應用程式發出寫入保存在緩衝區寫入儲存媒體

圖7:這是使用緩衝時的概念圖,不是某個產品的完成條件說明。

但是,不能說「停在99%一定是在將快取寫回」。究竟是在驗證、寫回,還是等待其他作業,需要檢查應用程式設計或記錄。不要只看進度數字,就推斷可以拔掉USB裝置或切斷電源。

7. 是工作真的停了,還是只有顯示停了?

在Windows的WPF應用程式中,UI執行緒上的Dispatcher負責處理介面工作。長時間占用該執行緒,會讓畫面更新和輸入回應延遲。實際執行的工作,與把結果顯示到畫面上的工作,需要分開考慮。8

從實際處理到進度顯示的路徑即使實際工作已回報進度,如果UI端無法處理更新,變化也不會反映到畫面上。實際處理回報進度UI處理更新反映到畫面UI執行緒阻塞

圖8:顯示停止,不一定代表實際處理也停止。

反過來,如果介面動畫與實際處理獨立運作,實際工作處於等待狀態時,圓形動畫仍然可以轉動。「在動就正常,不動就故障」這樣的二分判斷並不夠。

觀察什麼 可以取得什麼線索 光憑它無法確定什麼
已處理數量、資料量、階段名稱是否變化 已回報的工作進展 整個工作最後能否成功
記錄中的時間、對象與錯誤 記錄顯示在哪一步做什麼 沒有寫入記錄的處理是否停止
相關處理程序的CPU、磁碟與網路活動 當時的資源使用情況 是正常推進、等待,還是無效反覆
其他視窗中的確認提示 是否在等待使用者輸入 所有可能的停滯原因

本文建議先記錄畫面與開始時間,檢查是否有等待確認的提示,再比較處理數量或記錄是否變化。工作管理員裡的數值只當作輔助證據。考慮結束工作前,應先確認應用程式的取消方式,以及未完成的輸出會如何處理。

.NET的取消機制也是合作式的:發出取消要求不會立即停止處理,處理端需要回應要求。9 因此,「正在取消」與「已取消」也應該是不同狀態。僅憑進度顯示,無法得出一個人人適用的「超過幾分鐘就可以安全強制結束」的數值。

8. 比較同一工作的三種顯示方式

開啟進度列比較示範

這個示範透過按鈕切換虛擬工作的觀測時點。不需要等待,也不會讀取、寫入或上傳檔案。第一個案例是「99個小檔案加1個大檔案」。同一時點的檔案數量進度列、傳輸量進度列和工作整體狀態會並列呈現。

傳輸最後一個大檔案的過程中,依數量計算的進度列仍停在99%,但依資料量計算的進度列會繼續增加。傳輸量達到100%時,整體狀態仍是驗證中;推進到下一個觀測時點,才會變成成功。可以由此看出,光是換一種顯示方式,同一工作就可能看起來像停住了,也可能明顯正在推進。

如何閱讀比較示範將同一虛擬工作在同一時點的觀測值,分別交給檔案數量、傳輸量和整體狀態三種顯示。一個虛擬工作同一觀測時點檔案數量比例傳輸量比例工作整體狀態

圖9:示範比較的是一個工作的三種檢視方式,而不是三個不同的工作。

另一個案例重現第2節的速度下降,顯示10秒如何算成79秒。所用計算如下。這只是用來學習的簡單預測,沒有實作實際應用程式中的重試、平行處理或分階段預測。

function estimateSeconds(remaining, rate) {
  if (!Number.isFinite(remaining) || remaining < 0) return null;
  if (remaining === 0) return 0;
  if (!Number.isFinite(rate) || rate <= 0) return null;
  const seconds = remaining / rate;
  return Number.isFinite(seconds) ? seconds : null;
}

傳入remaining與rate時,必須保持單位一致,例如MiB與MiB/s。這裡回傳0只表示估算範圍內已經沒有剩餘工作量,不代表應用程式的整個工作已成功。剩餘量為正且速度為0或未知時回傳null,避免把「無法估算」誤認為「剩餘0秒」。

9. 開發者應追求「不誤導」,而不只是「看起來精確」

對於本文討論的設計,我會分別保存並顯示目前階段、已測得的工作量、有依據時才提供的剩餘時間,以及成功、失敗或取消的結果。即使把各階段百分比合成一根進度列,「傳輸80%、驗證20%」也只是設計上的權重,不保證這次耗時就依這個比例分配。

依觀測資訊建立進度介面將目前階段、實際測量的數量、有依據時的剩餘時間估算,以及最終結果分開顯示。工作提供的資訊目前階段已測量工作量與總量條件滿足時的估算成功、失敗或取消

圖10:在介面上也要區分觀測結果與預測。

例如,「正在驗證:400 / 1,000項,正在估算剩餘時間」即使沒有秒數也很有用。如果顯示更新時間,「進度最後增加的時間」與「介面最後成功通訊的時間」也不是同一回事。不要只更新後者,卻讓使用者誤以為實際工作一直在正常推進。

無障礙設計也是同樣的思路。Web中的progress元素可以透過省略值來表示不確定進度。10 對於自訂的ARIA進度顯示,值未知時應省略aria-valuenow,並提供能說明「是什麼的進度」的無障礙名稱。11 不只視覺顯示,螢幕閱讀器讀出的資訊也應該如實反映目前已知的範圍。

10. 常見問題

一直顯示剩餘1秒,是發生故障了嗎?

光看顯示無法判斷。要區分估算失準、其他階段、畫面更新延遲,以及工作實際停滯。「經常這樣,所以不用管」與「超過1秒了,所以一定壞了」都太過武斷。

99%是否代表只剩總時間的1%?

不是。不論比例的單位是檔案還是位元組,每個單位需要的時間都不一定固定。不能用已經過的總時間乘以1%,來求出剩餘時間。

圓形動畫還在轉,就代表正常嗎?

表示正在運作的動畫,與工作實際推進的證據是兩回事。應搭配已完成量、階段和記錄來判斷。只看動畫不能保證工作能正常完成。

不知道剩餘時間,就不能顯示進度嗎?

知道總量與已完成量,就可以顯示比例。預測不可靠時只省略秒數。如果連總量都未知,就採用不確定進度顯示,並附上階段名稱與已處理數量。

11. 總結:剩餘時間是預測,完成狀態是結果

「剩餘1秒」持續很久,不是因為電腦不會計算1秒,而是它要依已觀察到的工作量,估計還沒發生的處理時間。再加上計數單位、目標尋找、最後的階段與畫面更新延遲,情況就更複雜。

如何解讀遲遲不結束的進度顯示依序確認比例在衡量什麼、預測速度是否變化、是否還有其他階段,以及停住的是顯示還是實際處理。衡量什麼的比例預測速度是否改變是否還剩其他階段顯示停滯還是處理停滯

圖11:把對數字的不滿,拆成可以查證的問題。

使用者應關注階段與變化,而不只盯著數字;開發者不要把工作量、預測和結果混在一起。比起一直猜準「還剩1秒」,更重要的是說明現在正在發生什麼,以及哪些事情仍然未知。這才是好的進度顯示該做的事。

參考資料

  1. Microsoft Learn, LPPROGRESS_ROUTINE callback function. 複製進度回呼所提供位元組數的意義。 ↩

  2. Microsoft, The Old New Thing, Why does the copy dialog give such horrible estimates?. 2004年的解釋,用來說明預測未來速度的困難,不是目前Windows內部實作的規格說明。 ↩

  3. Microsoft Learn, Slow SMB files transfer speed. 小檔案傳輸反覆產生的檔案建立與通訊負擔。 ↩

  4. Microsoft Learn, Progress controls. 確定與不確定進度的控制項。 ↩

  5. Microsoft Learn, Progress Bars. 桌面應用程式進度顯示的設計指南。 ↩

  6. Microsoft Learn, File Caching. 檔案快取與寫入行為。 ↩

  7. Microsoft Learn, FlushFileBuffers function. 把檔案緩衝資訊傳送到裝置的API。 ↩

  8. Microsoft Learn, Threading model. WPF的Dispatcher與UI執行緒回應能力。 ↩

  9. Microsoft Learn, Cancellation in Managed Threads. .NET的合作式取消。 ↩

  10. WHATWG, The progress element. HTML進度元素與不確定狀態。 ↩

  11. W3C, WAI-ARIA 1.2: progressbar. 無障礙進度資訊的名稱與數值規則。 ↩

共用相同標籤的最新文章。能以相近的主題延伸理解。

與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。

本文連結到以下服務頁面,歡迎從最接近的入口查看。

常見問題

整理諮詢這個主題時常見的問題。

一直顯示剩餘1秒,是發生故障了嗎?
光看顯示無法判斷。需要區分速度預測失準、最後的處理階段、畫面沒有更新,以及工作確實停滯。檢查處理數量與記錄是否變化,以及是否有等待確認的視窗,不要只因剩餘1秒這個數字就強制結束應用程式。
進度達到99%,是否代表只剩總時間的1%?
不是。99%的意義取決於它衡量的是數量、資料量還是階段權重,而且每個單位的處理時間不一定相同。進度百分比是工作量的比例,不是剩餘時間本身。
圓形動畫還在轉,就能證明工作正常嗎?
不能。如果畫面動畫與實際處理獨立運作,工作處於等待狀態時,動畫也能繼續轉動。應把動畫與處理數量、階段變化、記錄等實際進展的證據分開看。
無法準確估算剩餘時間,就不能顯示進度列嗎?
可以顯示。只要知道總量與已完成量,就能顯示比例;速度不穩定時可以只省略剩餘時間。如果連總量也未知,應顯示階段或已處理數量,而不是編造百分比。

作者檔案

本文作者的個人檔案頁面。

Go Komura

小村軟體有限公司 代表

以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。

回到部落格一覽