Pytest-Benchmark:高效能測試的基準分析工具

更新日期: 2025 年 2 月 13 日

在軟體開發過程中,除了功能測試 (Functional Testing) 之外,效能測試 (Performance Testing) 也是不可或缺的一環。

效能問題可能會影響用戶體驗,甚至導致系統崩潰。

因此,開發者需要一個可靠的工具來測量和比較程式碼的執行效率。

pytest-benchmarkpytest 的一個外掛套件,專門用於基準測試 (Benchmarking)。

它能夠幫助開發者分析程式碼的效能瓶頸,提供詳細的測試報告,並支援多種比較方法,以確保新版本的程式碼不會因為改動而降低效能。

本文將詳細介紹 pytest-benchmark 的功能、安裝方式、使用方法,以及如何透過它來提升程式碼的效能。

基準測試 (Benchmarking) 是什麼?

基準測試(Benchmarking)是一種測量系統或程式碼效能的方法,主要用來評估特定操作的執行時間或資源消耗。

透過基準測試,開發者可以量化不同演算法、函式或系統的效能,找出可能的效能瓶頸,並比較不同版本或不同技術的效能表現。

為什麼需要基準測試?

在軟體開發中,除了確保程式正確運行(功能測試),還需要確保它運行得夠快、夠高效(效能測試)。

以下是基準測試的重要性:

  • 找出效能瓶頸:幫助開發者確定哪些部分的程式碼執行時間過長,需要進行最佳化。
  • 比較不同方案的效能:當有多種演算法或解決方案時,可以透過基準測試來選擇最優的實作方式。
  • 確保改版後效能不下降:在開發新功能或進行程式碼重構時,基準測試能確保新版本的效能不會退步。
  • 提供量化數據:能夠以數據來證明某段程式碼的優劣,而不只是靠直覺判斷。

基準測試的常見類型

基準測試可以應用在不同層面,主要分為以下幾種類型:

函式級別 (Microbenchmarking)

測試單一函式或小範圍的程式碼片段,例如:

  • 測量某個排序演算法的執行時間
  • 計算特定數學運算的效能
  • 測試某個 API 呼叫的回應時間

💡 適合用來最佳化單一函式或演算法

系統級別 (Macrobenchmarking)

測試整個應用程式的效能,例如:

  • 測量一個 Web 伺服器處理請求的速度
  • 測試資料庫查詢的效能
  • 測量整體應用程式的執行效率

💡 適合用來評估系統整體的效能表現

比較測試 (Regression Benchmarking)

這種基準測試專門用來比較不同版本的效能,例如:

  • 比較某段程式碼在 Python 3.8 和 Python 3.11 上的執行時間
  • 測試程式碼改版前後,效能是否有所提升或下降

💡 適合用來確保新版本不會降低效能

如何進行基準測試?

進行基準測試時,通常會遵循以下步驟:

  1. 定義測試目標:明確要測試的函式或系統模組,例如:「我要測試這段排序演算法的效能」。
  2. 選擇測試工具:根據測試需求選擇適合的工具,例如 timeitpytest-benchmarkcProfile 等。
  3. 設計測試案例:確保測試條件一致,例如相同的輸入數據、相同的環境等,以避免測試結果受到其他變數影響。
  4. 執行測試並收集數據:重複執行測試以獲得穩定的結果,並記錄執行時間、CPU 使用率、記憶體消耗等資訊。
  5. 分析與比較結果:找出效能瓶頸,並比較不同方案的測試結果,決定是否需要進行最佳化。

Python 中的基準測試工具

在 Python 中,有多種工具可用來進行基準測試,以下是幾個常見的選擇:

工具特色適用範圍
timeitPython 內建模組,適用於簡單的執行時間測試小型函式的測試
pytest-benchmarkpytest 插件,提供詳細的基準測試數據單元測試中的基準測試
cProfilePython 內建的效能分析工具測試函式內部的 CPU 運行時間
line_profiler逐行分析程式碼執行時間找出程式碼的效能瓶頸
memory_profiler測試記憶體使用量內存效能分析

Pytest-Benchmark 是什麼?

簡介

pytest-benchmarkpytest 的一個插件,專門用來測量 Python 函式的執行時間。它能夠記錄多次執行的結果,並提供統計資訊,如平均時間、中位數、標準差等,幫助開發者分析效能變化。

與傳統的 timeitcProfile 等工具不同,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.inipyproject.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

範例解析

  1. 引入 benchmark fixturebenchmarkpytest-benchmark 提供的測試工具,可以測量指定函式的執行時間。
  2. 使用 benchmark 測試函式
    • benchmark(time.sleep, 0.1) 會執行 time.sleep(0.1) 並測量其執行時間。
    • pytest-benchmark 會自動多次運行該函式,然後計算出平均執行時間、中位數、標準差等統計數據,以提高測試結果的可靠性。
  3. 驗證結果
    • 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

🔹 解析:

  • benchmarkpytest-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)

範例解析

  1. bubble_sort(arr):使用 冒泡排序,時間複雜度為 O(n^2),適用於小型數據集。
  2. quick_sort(arr):使用 快速排序,時間複雜度為 O(n log n),通常比冒泡排序快。
  3. test_bubble_sort(benchmark)
    • 產生一個包含 100 個隨機數的數列
    • 使用 benchmark(bubble_sort, arr) 測量 bubble_sort 的執行時間
  4. 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,讓你的程式碼跑得更快、更穩! 🚀

Similar Posts