Pytest-Benchmark:高效能測試的基準分析工具
更新日期: 2025 年 2 月 13 日
本文為 AI 描述優化 api 設計 系列文,第 1 篇:
- 如何設計一個商品描述優化 API?—— 完整指南
- 設計 AI 優化商品描述的 Prompt
- FastAPI:高效且易用的 Python Web 框架
- 介紹 Uvicorn:高效能 ASGI 伺服器
- Uvicorn 監聽 0.0.0.0,但為何 API 只能用 127.0.0.1 訪問?
- Tenacity:強大的 Python 重試機制庫
- FastAPI 建立商品描述優化 API
- 介紹 pytest:Python 測試框架的強大選擇
- Python httpx 完整指南:高效的 HTTP 客戶端
- Pytest-Benchmark:高效能測試的基準分析工具 👈進度
- 深入解析 monkeypatch:Python 測試中的強大工具
- API 自動化測試與效能優化
建議閱讀本文前,先具備 聊天機器人-建議回復 相關系列文的概念。
在軟體開發過程中,除了功能測試 (Functional Testing) 之外,效能測試 (Performance Testing) 也是不可或缺的一環。
效能問題可能會影響用戶體驗,甚至導致系統崩潰。
因此,開發者需要一個可靠的工具來測量和比較程式碼的執行效率。
pytest-benchmark
是 pytest
的一個外掛套件,專門用於基準測試 (Benchmarking)。
它能夠幫助開發者分析程式碼的效能瓶頸,提供詳細的測試報告,並支援多種比較方法,以確保新版本的程式碼不會因為改動而降低效能。
本文將詳細介紹 pytest-benchmark
的功能、安裝方式、使用方法,以及如何透過它來提升程式碼的效能。
基準測試 (Benchmarking) 是什麼?
基準測試(Benchmarking)是一種測量系統或程式碼效能的方法,主要用來評估特定操作的執行時間或資源消耗。
透過基準測試,開發者可以量化不同演算法、函式或系統的效能,找出可能的效能瓶頸,並比較不同版本或不同技術的效能表現。
為什麼需要基準測試?
在軟體開發中,除了確保程式正確運行(功能測試),還需要確保它運行得夠快、夠高效(效能測試)。
以下是基準測試的重要性:
- 找出效能瓶頸:幫助開發者確定哪些部分的程式碼執行時間過長,需要進行最佳化。
- 比較不同方案的效能:當有多種演算法或解決方案時,可以透過基準測試來選擇最優的實作方式。
- 確保改版後效能不下降:在開發新功能或進行程式碼重構時,基準測試能確保新版本的效能不會退步。
- 提供量化數據:能夠以數據來證明某段程式碼的優劣,而不只是靠直覺判斷。
基準測試的常見類型
基準測試可以應用在不同層面,主要分為以下幾種類型:
函式級別 (Microbenchmarking)
測試單一函式或小範圍的程式碼片段,例如:
- 測量某個排序演算法的執行時間
- 計算特定數學運算的效能
- 測試某個 API 呼叫的回應時間
💡 適合用來最佳化單一函式或演算法
系統級別 (Macrobenchmarking)
測試整個應用程式的效能,例如:
- 測量一個 Web 伺服器處理請求的速度
- 測試資料庫查詢的效能
- 測量整體應用程式的執行效率
💡 適合用來評估系統整體的效能表現
比較測試 (Regression Benchmarking)
這種基準測試專門用來比較不同版本的效能,例如:
- 比較某段程式碼在 Python 3.8 和 Python 3.11 上的執行時間
- 測試程式碼改版前後,效能是否有所提升或下降
💡 適合用來確保新版本不會降低效能
如何進行基準測試?
進行基準測試時,通常會遵循以下步驟:
- 定義測試目標:明確要測試的函式或系統模組,例如:「我要測試這段排序演算法的效能」。
- 選擇測試工具:根據測試需求選擇適合的工具,例如
timeit
、pytest-benchmark
、cProfile
等。 - 設計測試案例:確保測試條件一致,例如相同的輸入數據、相同的環境等,以避免測試結果受到其他變數影響。
- 執行測試並收集數據:重複執行測試以獲得穩定的結果,並記錄執行時間、CPU 使用率、記憶體消耗等資訊。
- 分析與比較結果:找出效能瓶頸,並比較不同方案的測試結果,決定是否需要進行最佳化。
Python 中的基準測試工具
在 Python 中,有多種工具可用來進行基準測試,以下是幾個常見的選擇:
工具 | 特色 | 適用範圍 |
---|---|---|
timeit | Python 內建模組,適用於簡單的執行時間測試 | 小型函式的測試 |
pytest-benchmark | pytest 插件,提供詳細的基準測試數據 | 單元測試中的基準測試 |
cProfile | Python 內建的效能分析工具 | 測試函式內部的 CPU 運行時間 |
line_profiler | 逐行分析程式碼執行時間 | 找出程式碼的效能瓶頸 |
memory_profiler | 測試記憶體使用量 | 內存效能分析 |
Pytest-Benchmark 是什麼?
簡介
pytest-benchmark
是 pytest
的一個插件,專門用來測量 Python 函式的執行時間。它能夠記錄多次執行的結果,並提供統計資訊,如平均時間、中位數、標準差等,幫助開發者分析效能變化。
與傳統的 timeit
或 cProfile
等工具不同,pytest-benchmark
可以與 pytest
測試框架無縫整合,讓基準測試變得更加方便且易於管理。
主要特點
- 自動重複測試:可多次執行相同測試,以減少偶發性誤差。
- 詳細統計數據:包括執行時間、標準差、最佳/最差結果等。
- 與 pytest 無縫整合:可與現有的
pytest
測試一起運行,無需額外設置。 - 結果比較:可比較不同版本的測試結果,確保效能沒有下降。
- 多種輸出格式:支持 JSON 和 CSV 格式,方便進行分析與可視化。
Pytest-Benchmark 的安裝與設定
安裝
在使用 pytest-benchmark
之前,需要先安裝 pytest
,然後再安裝 pytest-benchmark
:
pip install pytest pytest-benchmark
安裝完成後,可以使用以下指令確認是否安裝成功:
pytest --help | grep benchmark
如果成功安裝,應該能夠看到 benchmark
相關的選項。
設定檔案 (可選)
雖然 pytest-benchmark
預設提供了合理的設定,但如果需要更進階的設定,可以在 pytest.ini
或 pyproject.toml
中加入相關配置,例如:
[pytest]
addopts = --benchmark-sort=name
這樣執行基準測試時,結果會按照名稱排序,方便比較不同測試的結果。
Pytest-Benchmark 的基本使用方法
撰寫基準測試
基準測試的撰寫方式與一般 pytest
測試相似,只需要在測試函式中引入 benchmark
fixture,並使用它來測量函式或程式碼片段的執行時間。
pytest-benchmark
會自動重複執行測試多次,以減少環境因素造成的誤差,確保結果的準確性。
以下是一個簡單的基準測試範例,它用來測試 time.sleep(0.1)
的執行時間:
import time
def test_my_function(benchmark):
result = benchmark(time.sleep, 0.1)
assert result is None
範例解析
- 引入
benchmark
fixture:benchmark
是pytest-benchmark
提供的測試工具,可以測量指定函式的執行時間。 - 使用
benchmark
測試函式:benchmark(time.sleep, 0.1)
會執行time.sleep(0.1)
並測量其執行時間。pytest-benchmark
會自動多次運行該函式,然後計算出平均執行時間、中位數、標準差等統計數據,以提高測試結果的可靠性。
- 驗證結果:
assert result is None
用來確認time.sleep()
沒有回傳任何值。- 一般來說,基準測試主要關注執行時間,因此通常不會對返回值進行驗證,但如果函式有特定的輸出要求,也可以加入
assert
來確保正確性。
這樣的測試可以幫助我們確認 time.sleep(0.1)
的實際延遲時間是否接近 100 毫秒,並觀察其穩定性。
補充:什麼是 fixture?
在
pytest
測試框架中,fixture 是一種特殊的函式,用來提供測試函式所需的環境設置、測試數據或依賴資源。它的主要作用是在測試開始前執行某些初始化動作,並在測試結束後執行清理,確保測試環境的一致性和可重複性。
簡單來說,
fixture
可以幫助測試:
- 準備測試環境(例如建立測試資料、連接資料庫、初始化變數)
- 提供測試所需的資源(例如傳入特定的測試數據)
- 在測試結束後進行清理(例如關閉資料庫連線、刪除測試檔案)
fixture 的基本用法
在
pytest
中,fixture
透過@pytest.fixture
裝飾器 (decorator) 來定義,然後在測試函式中作為參數傳入。範例 1:簡單的 fixture
import pytest @pytest.fixture def sample_data(): return {"name": "Alice", "age": 25} def test_example(sample_data): assert sample_data["name"] == "Alice" assert sample_data["age"] == 25
🔹 解析:
@pytest.fixture
標記sample_data
為一個 fixture,它會回傳一個字典{"name": "Alice", "age": 25}
。- 測試函式
test_example(sample_data)
會自動接收sample_data
,並使用它來進行測試。pytest
會在測試執行時,自動調用sample_data()
,並將其結果傳入test_example()
。fixture 在基準測試 (benchmark) 的角色
在
pytest-benchmark
插件中,benchmark
也是一種 fixture。它的功能是測量測試函式的執行時間,並自動進行多次測試來確保結果的準確性。範例 2:使用
benchmark
fixture 測量函式效能import time def test_my_function(benchmark): result = benchmark(time.sleep, 0.1) assert result is None
🔹 解析:
benchmark
是pytest-benchmark
提供的內建 fixture。benchmark(time.sleep, 0.1)
會多次執行time.sleep(0.1)
,測量其執行時間,並計算統計數據(如平均值、中位數、標準差等)。pytest-benchmark
會確保測試結果的穩定性,並自動過濾執行環境的影響(如 CPU 負載波動)。
執行基準測試
撰寫好基準測試後,可以透過 pytest
執行測試,並取得詳細的效能報告。
基本執行
執行以下指令來運行所有基準測試:
pytest --benchmark-only
此指令會運行所有包含 benchmark
的測試函式,並在終端機中輸出測試結果,包括平均執行時間、標準差等。
存儲測試結果
如果希望將測試結果存成 JSON 檔案,供後續分析或比較使用,可以執行:
pytest --benchmark-json=benchmark_results.json
這將會把測試結果存為 benchmark_results.json
,日後可以透過 pytest-benchmark compare
來比較不同版本的效能表現。
範例輸出結果
執行基準測試後,終端機可能會顯示類似以下的輸出結果:
-------------------------------------------------------------------------------------- benchmark: 1 tests --------------------------------------------------------------------------------------
Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS (Kops/s) Rounds Iterations
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_my_function 100.2 101.5 100.7 0.5 100.6 0.4 0;0 9.93 10 1
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
這裡的數據解釋如下:
Min
/Max
:最小與最大執行時間(毫秒)Mean
:平均執行時間StdDev
:標準差,表示測試結果的穩定性Median
:中位數,能反映測試的典型執行時間OPS (Kops/s)
:每秒可執行的操作次數(千次)Rounds
/Iterations
:測試的執行回合數與迭代次數
這些數據可以幫助開發者評估函式的執行時間,並確認其穩定性。
進階測試:比較不同實作的效能
pytest-benchmark
也可以用來比較不同函式的執行效能,例如比較兩種不同排序演算法的速度,以判斷哪種方式更高效。
範例:比較 Bubble Sort 與 Quick Sort
import random
def bubble_sort(arr):
"""冒泡排序:每次循環將最大值推到最後"""
n = len(arr)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
def quick_sort(arr):
"""快速排序:選擇一個基準點,並遞迴排序"""
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
def test_bubble_sort(benchmark):
"""基準測試:測試 Bubble Sort 的執行時間"""
arr = [random.randint(0, 1000) for _ in range(100)]
benchmark(bubble_sort, arr)
def test_quick_sort(benchmark):
"""基準測試:測試 Quick Sort 的執行時間"""
arr = [random.randint(0, 1000) for _ in range(100)]
benchmark(quick_sort, arr)
範例解析
bubble_sort(arr)
:使用 冒泡排序,時間複雜度為O(n^2)
,適用於小型數據集。quick_sort(arr)
:使用 快速排序,時間複雜度為O(n log n)
,通常比冒泡排序快。test_bubble_sort(benchmark)
:- 產生一個包含 100 個隨機數的數列
- 使用
benchmark(bubble_sort, arr)
測量bubble_sort
的執行時間
test_quick_sort(benchmark)
:- 同樣產生 100 個隨機數
- 使用
benchmark(quick_sort, arr)
測量quick_sort
的效能
執行測試與比較結果
執行以下指令來運行基準測試並比較兩種演算法的效能:
pytest --benchmark-only
假設測試結果如下:
-------------------------------------------------------------------------------------- benchmark: 2 tests --------------------------------------------------------------------------------------
Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS (Kops/s) Rounds Iterations
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_bubble_sort 10.3 12.5 11.4 0.8 11.3 0.9 0;0 87.72 10 1
test_quick_sort 0.8 1.2 0.9 0.1 0.9 0.1 0;0 1111.11 10 1
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
從結果可以看出:
- Bubble Sort 平均執行時間約 11.4 毫秒,每秒最多執行 87.72 次。
- Quick Sort 平均執行時間僅 0.9 毫秒,每秒可執行超過 1111 次。
這顯示 Quick Sort 的效能遠優於 Bubble Sort,適合用於較大的數據集。
進階功能與最佳實踐
設定基準測試參數
可透過 @pytest.mark.benchmark
設定基準測試的參數,例如:
import time
import pytest
@pytest.mark.benchmark(min_rounds=5, disable_gc=True)
def test_optimized_function(benchmark):
benchmark(time.sleep, 0.01)
min_rounds=5
:至少執行 5 次測試,確保結果穩定。disable_gc=True
:禁用 Python 的垃圾回收機制,避免影響測試結果。
延伸閱讀:理解 Python 中的引用計數機制(Reference Counting)
比較不同測試結果
執行多次測試後,可用 pytest-benchmark compare
來比較不同版本的效能變化。例如:
pytest-benchmark compare benchmark_results.json
這樣可以確保新版本的程式碼不會因改動而降低效能。
結論
pytest-benchmark
是一款強大的基準測試工具,能夠幫助開發者測量 Python 程式的執行時間,找出效能瓶頸,並確保改版後的效能不會退步。
它的優勢在於與 pytest
無縫整合,使用簡單且功能強大,適合用於函式級別的效能測試。
透過 pytest-benchmark
,開發者可以輕鬆地比較不同實作的效能,進行最佳化,確保應用程式運行更高效。
如果你的專案對效能有要求,不妨試試 pytest-benchmark
,讓你的程式碼跑得更快、更穩! 🚀