天天看點

應該熟悉的shell性能測試資料

作者:SuperOps
應該熟悉的shell性能測試資料

本文内容概要

參數擴充

0m2.828s printf -v i '%s ' {1..1000000}; time { : $i; }
0m6.885s i=( {1..1000000} ); time { : "${i[@]}"; }

這個壓測結果涉及了兩種在Shell腳本中擴充多個參數的方法,即使用 printf 指令和使用花括号文法。下面對每個方法的表現進行分析:

  1. ​printf -v i '%s ' {1..1000000}; time { : $i; }​

該方法使用 printf​ 指令将數字清單轉換為一個字元串,并将其存儲在變量 i​ 中。在這個測試中,使用了 time​ 指令來記錄執行時間,并将空指令 :​ 作為測試目标。該方法的執行時間為 0m2.828s。

  1. ​i=( {1..1000000} ); time { : "${i[@]}"; }​

該方法使用花括号文法 {1..1000000}​ 将數字清單轉換為一個數組,并将其存儲在變量 i​ 中。在這個測試中,使用了 time​ 指令來記錄執行時間,并将空指令 :​ 作為測試目标。該方法的執行時間為 0m6.885s。

結論:使用 printf 指令将數字清單轉換為字元串,并使用引号括起來的變量名稱作為測試目标的方法表現最佳,執行時間最短。另外,在使用變量時,盡可能地使用引号來避免意外的結果或錯誤。使用花括号文法也是一種不錯的方法,但其執行時間較長,可能會在處理大量資料時影響性能。

Testing測試塊

0m1.099s time for i in {1..100000}; do [[ -d . ]]; done
0m2.110s time for i in {1..100000}; do [ -d . ]; done
3m47.509s time for i in {1..100000}; do /bin/test -d .; done

這個壓測結果涉及了三種判斷目前目錄是否為一個目錄的方法,即使用 [[ -d . ]]​、[ -d . ]​ 和 /bin/test -d .​。下面對每個方法的表現進行分析:

  1. ​time for i in {1..100000}; do [[ -d . ]]; done​

該方法使用雙方括号 [[ ]]​ 和 for​ 循環來判斷目前目錄是否為一個目錄。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m1.099s。

需要注意的是,雙方括号 [[ ]]​ 是一個關鍵字,其具有特殊的解析規則,可以用于比較複雜的條件測試。

  1. ​time for i in {1..100000}; do [ -d . ]; done​

該方法使用單方括号 [ ]​ 和 for​ 循環來判斷目前目錄是否為一個目錄。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m2.110s。

需要注意的是,單方括号 [ ]​ 是一個内置指令,其解析規則比雙方括号 [[ ]]​ 稍微簡單一些。

  1. ​time for i in {1..100000}; do /bin/test -d .; done​

該方法使用 /bin/test​ 指令和 for​ 循環來判斷目前目錄是否為一個目錄。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了100000次。該方法的執行時間為 3m47.509s。

需要注意的是,/bin/test​ 指令是一個外部指令,每次調用都需要啟動一個新的程序,是以其執行速度比内置指令和關鍵字慢得多。

結論:使用雙方括号 [[ ]] 的方法表現最佳,執行時間最短。使用單方括号 [ ]​ 的方法也表現不錯,但是速度較慢。使用外部指令 /bin/test​ 的方法速度最慢,不建議使用。

字元串操作

0m0.149s path=/foo/bar; time for i in {1..10000}; do : "${path##*/}"; done
0m19.284s path=/foo/bar; time for i in {1..10000}; do basename "$path" >/dev/null; done
0m21.289s path=/foo/bar; time for i in {1..10000}; do expr "$path" : './.)' >/dev/null; done
0m24.789s path=/foo/bar; time for i in {1..10000}; do sed 's,.*/,,' <<< "$path" >/dev/null; done

這個壓測結果涉及了四種從一個路徑中提取檔案名的方法,即使用參數擴充 ${path##*/}​、basename​ 指令、expr​ 指令和 sed​ 指令。下面對每個方法的表現進行分析:

  1. ​path=/foo/bar; time for i in {1..10000}; do : "${path##*/}"; done​

該方法使用參數擴充 ${path##*/}​ 和 for​ 循環來從路徑中提取檔案名。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m0.149s。

需要注意的是,${path##*/}​ 是一種快速提取檔案名的方法,其可以盡可能地避免啟動新的程序。

  1. ​path=/foo/bar; time for i in {1..10000}; do basename "$path" >/dev/null; done​

該方法使用 basename​ 指令和 for​ 循環來從路徑中提取檔案名。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m19.284s。

需要注意的是,basename​ 指令是一個外部指令,每次調用都需要啟動一個新的程序,是以其執行速度較慢。

  1. ​path=/foo/bar; time for i in {1..10000}; do expr "$path" : '.*/.*\)' >/dev/null; done​

該方法使用 expr​ 指令和 for​ 循環來從路徑中提取檔案名。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m21.289s。

需要注意的是,expr​ 指令是一個外部指令,每次調用都需要啟動一個新的程序,是以其執行速度較慢。

  1. ​path=/foo/bar; time for i in {1..10000}; do sed 's,.*/,,' <<< "$path" >/dev/null; done​

該方法使用 sed​ 指令和 for​ 循環來從路徑中提取檔案名。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m24.789s。

需要注意的是,sed​ 指令是一個外部指令,每次調用都需要啟動一個新的程序,是以其執行速度較慢。

結論:在這些測試中,使用 ${path##*/} 的方法表現最佳,執行時間最短。使用 basename​、expr​ 和 sed​ 指令的方法都需要啟動新的程序,是以其執行速度較慢,不建議使用。

參數疊代

0m0.239s set -- {1..100000}; time for x; do <(); done
0m0.298s set -- {1..100000}; time for x; do :; done
0m12.759s set -- {1..100000}; n=1; time while ((${!n+n++})); do :; done
0m15.700s set -- {1..100000}; n=1; time while [[ ${!n+_} ]]; do ((++n)); done
0m18.560s set -- {1..100000}; time while shift; do :; done
0m38.831s set -- {1..100000}; n=1; time while ((n++<$#)); do :; done

這個壓測結果涉及了五種空操作的方法,即使用空的程序替換 <()​、冒号指令 :​、循環、移動變量和計算參數個數。下面對每個方法的表現進行分析:

  1. ​set -- {1..100000}; time for x; do <(); done​

該方法使用空的程序替換 <()​ 和 for​ 循環來進行空操作。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m0.239s。

需要注意的是,空的程序替換 <()​ 是一種快速進行空操作的方法,其可以盡可能地避免啟動新的程序。

  1. ​set -- {1..100000}; time for x; do :; done​

該方法使用冒号指令 :​ 和 for​ 循環來進行空操作。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m0.298s。

需要注意的是,冒号指令 :​ 是一種常用的進行空操作的方法,其執行速度比較快。

  1. ​set -- {1..100000}; n=1; time while ((${!n+n++})); do :; done​

該方法使用循環和參數擴充 ${!n}​ 和 ${n++}​ 來進行空操作。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m12.759s。

需要注意的是,使用參數擴充 ${!n}​ 和 ${n++}​ 的方法在循環中進行大量的變量操作,是以其執行速度較慢。

  1. ​set -- {1..100000}; n=1; time while [[ ${!n+_} ]]; do ((++n)); done​

該方法使用循環和參數擴充 ${!n}​ 來進行空操作。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m15.700s。

需要注意的是,使用參數擴充 ${!n}​ 的方法在循環中進行大量的變量操作,是以其執行速度較慢。

  1. ​set -- {1..100000}; time while shift; do :; done​

該方法使用循環和 shift​ 指令來進行空操作。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了多次,但執行時間不穩定。該方法的執行時間可能會受到緩存的影響,重複移動大參數清單可能會導緻緩存未命中。執行時間在不同的測試中可能會有很大的差别。

需要注意的是,使用 shift​ 指令的方法在循環中進行大量的變量操作,是以其執行速度較慢。此外,該方法的執行時間可能會受到緩存的影響。

  1. ​set -- {1..100000}; n=1; time while ((n++<$#)); do :; done​

該方法使用循環和參數擴充 $#​ 來進行空操作。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m38.831s。

需要注意的是,使用參數擴充 $#​ 的方法需要在每次循環中計算參數的個數,是以其執行速度較慢。

結論:在這些測試中,使用空的程序替換 <() 的方法表現最佳,執行時間最短。使用冒号指令 : 也是一種執行速度較快的方法。其他方法的執行速度較慢,不建議使用。

不同的shell解釋器

0m0.021s time dash -c ':' -- {1..10000}
0m0.024s time ksh -c ':' -- {1..10000}
0m0.027s time zsh -c ':' -- {1..10000}
0m0.217s time bash -c ':' -- {1..10000}

這個壓測結果涉及了四種 shell 解釋器執行空指令 :​ 的方法,即使用 dash、ksh、zsh 和 bash。下面對每個方法的表現進行分析:

  1. ​time dash -c ':' -- {1..10000}​

該方法使用 dash shell 解釋器執行空指令 :​ 和 -c​ 選項來進行壓測。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m0.021s。

需要注意的是,dash 是一種輕量級的 shell 解釋器,其啟動速度比較快,是以在執行空指令時表現較優。

  1. ​time ksh -c ':' -- {1..10000}​

該方法使用 ksh shell 解釋器執行空指令 :​ 和 -c​ 選項來進行壓測。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m0.024s。

需要注意的是,ksh 是一種比較古老的 shell 解釋器,其啟動速度比較快,是以在執行空指令時表現較優。

  1. ​time zsh -c ':' -- {1..10000}​

該方法使用 zsh shell 解釋器執行空指令 :​ 和 -c​ 選項來進行壓測。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m0.027s。

需要注意的是,zsh 是一種功能比較強大的 shell 解釋器,其啟動速度較快,是以在執行空指令時表現較優。

  1. ​time bash -c ':' -- {1..10000}​

該方法使用 bash shell 解釋器執行空指令 :​ 和 -c​ 選項來進行壓測。在這個測試中,使用了 time​ 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m0.217s。

需要注意的是,bash 是一種功能比較強大的 shell 解釋器,其啟動速度較慢,特别是在使用長參數清單時,會導緻啟動時間更長。但是,一旦 bash 啟動完成,其執行空指令的速度與其他解釋器相當。

結論:dash、ksh 和 zsh 的啟動速度比較快,是以在執行空指令時表現較優。bash 的啟動速度較慢,但一旦啟動完成,其執行空指令的速度與其他解釋器相當。

總結

本文基于多個次元分别針對shell在不同場景下的多種使用方式進行了測試驗證,提供了很多測試驗證資料友善大家使用的時候注意。shell是一個非常靈活的解釋性腳本語言,其能夠動态調用外部程序執行任務,提供了靈活多變的執行方式,但是同時也埋了很多使用層面的坑,這也是為啥編寫《shell腳本程式設計最佳實踐》專欄的初衷,就是幫助大家快速了解到shell的各種坑通過該專欄作為一個避坑指南,幫助大家了解各種生産環境實踐,幫大家減少困惑。目前專欄還在持續更新中,優惠力度較大歡迎趁優惠購買。

繼續閱讀