有些你以為不會錯的直覺,實際上錯得離譜。

清理 750 萬個檔案資料夾文章提到,我選擇將 DIR 結果寫成檔案方便反覆測試調整,轉存過程用了一招將 DIR 結果同時輸出到檔案與螢幕,理由是看得到進度能化解等待焦慮,有益急躁老人心血管保健。但不少讀者提醒此舉讓原本很耗時的 DIR 轉存動作雪上加霜,乃不智之舉;而依我的直覺,重導到螢幕輸出屬記憶體與 UI 顯示作業,增加的資料處理與顯示運算與 DIR 運算量相比不算嚴重(事實證明這個直覺大錯特錯),不致嚴重拖累效能,反正不想觀察時將 CMD 視窗最小化便可省去晝面 Render 運算,久久再顯示窺探一下,藉此換取資訊掌握度應屬划算。

不過,後來研究 NTFS 檔案佔用磁碟大小 (Size On Disk),呼應了讀者朋友們的分析,DIR 是由 MFT 讀取檔案資訊,每筆記錄 1KB,750 萬個檔案 7.5GB,就算記錄分散各處,用機械硬碟用隨機讀取 4K QD32 標準 33MB/s 評估,DIR 怎麼也不該慢到數小時,莫非這一切是輸出導向到螢幕造成的?

要得到答案,做實驗吧。

【實驗方式】

用 RDP 連上 Windows 10 PC,借用上回建立的兩百萬個檔案當實驗材料,以 Cmder 執行 dir X:\2MFiles > dir.txt(結果直接轉存檔案) 及 dir X:\2MFiles | tee dir.txt(結果同時輸出到檔案及螢幕),後者測兩次,一次全程開啟 Cmder 觀察顯示文件、一次將 Cmder 最小化。每次測試前先重新開機,並等待五分鐘再跑測試,排除 Cache 效應及系統服務啟動初期的磁碟 IO。

測試程式如下:

del dir.txt
dir X:\2MFiles > dir.txt
powershell .\check.ps1

check.ps1 會檢查 dir.txt 的建立時間及最後寫入時間,計算 dir 耗用時間長短:

$st = (Get-Item dir.txt).CreationTime
$ed = (Get-Item dir.txt).LastWriteTime
"dir.txt size = $((Get-Item dir.txt).Length.ToString('n0'))"
"Start: $($st.ToString('HH:mm:ss.fff')) End: $($ed.ToString('HH:mm:ss.fff'))"
([TimeSpan]($ed - $st)).TotalMilliseconds.ToString('n3')

第一次測試 dir 結果純寫檔不輸出到螢幕,91.5s 完成。(以 200 萬乘每筆檔案記錄 1KB 推算,資料量 1GB,dir.txt 166KB 寫入部分不計,2G / 91s 約 22MB/s,蠻合理的)

用 tee 轉接到螢幕輸出,時間暴增 30 倍,花了 2700 秒:(註:我忘了 chcp 65001 切 UTF-8 字元變花,懶得重跑請大家無視)

那... 把命令列視窗關起來總會好一些吧?有! 快了三倍,但還是比不顯示慢了 10 倍:

心得:不要盲目相信自己的直覺,多聽不一樣的聲音,有任何疑義,回歸科學邏輯檢驗就對了,以免背離真理而不自知。(感謝 Vt, dir, Daniel Hou 的留言回饋)

以上的實驗結果,不代表 tee 這招不能用,要視應用情境。本案例是因為 200 萬筆檔案目錄資料的磁碟讀取時間遠底於列印 200 萬行文字所需時間,才會造成導向螢幕慢了 30 倍。換個上傳 1000 筆資料的情境,若上傳一筆耗時 5 秒,要顯示的資料量只有 1000 筆,那麼用 tee 存檔兼顯示不會有任何效能衝擊,可以安心使用。

回到最初的問題上,如果用 dir 接 tee 太慢,但看不到進度又會焦慮怎麼辦?其實有個好辦法,dir X:\2MFiles > dir.txt 直接導向,再用 tail 偷看最後幾行就好了,我阿呆~


Comments

# by Huang

不小心把簡單的事複雜化...XD

Post a comment