[Python]測試從3.8升級到3.12效益

更新 發佈閱讀 22 分鐘

測試Python 3.12.10版本與目前使用的3.87版本 差異

參考以下文章


Python 3.8.6 vs 3.12.10 上跑同一組程式,比較不同類型任務的效能差異。

成果彙整

raw-image

程式碼參考如下

🧮 數值運算密集 (CPU bound)

import time

def numeric_heavy(n=20_000_000):
s = 0
for i in range(n):
s += (i * i) ^ (i << 2) ^ (i >> 1)
return s

def benchmark(fn, *args, repeat=3):
times = []
for _ in range(repeat):
t0 = time.perf_counter()
fn(*args)
t1 = time.perf_counter()
times.append(t1 - t0)
print(f"{fn.__name__} -> {times}, avg={sum(times)/len(times):.4f}s")

if __name__ == "__main__":
benchmark(numeric_heavy, 20_000_000)

3.8.6

raw-image

3.12.10

raw-image

🔤 字串操作密集 (string processing)

import time

def string_heavy(n=500_000):
lst = []
for i in range(n):
s = str(i) * 3
lst.append(s[::-1]) # 字串翻轉
joined = ",".join(lst)
return len(joined)

def benchmark(fn, *args, repeat=3):
times = []
for _ in range(repeat):
t0 = time.perf_counter()
fn(*args)
t1 = time.perf_counter()
times.append(t1 - t0)
print(f"{fn.__name__} -> {times}, avg={sum(times)/len(times):.4f}s")

if __name__ == "__main__":
benchmark(string_heavy, 500_000)

3.8.6

raw-image

3.12.10

raw-image

📂 IO 密集 (檔案讀寫)

import time
import os

def io_heavy(filename="tmp_test.txt", n=1000, line_size=1000):
# 寫入
with open(filename, "w", encoding="utf-8") as f:
for i in range(n):
f.write("X" * line_size + "\n")
# 讀取
with open(filename, "r", encoding="utf-8") as f:
data = f.readlines()
os.remove(filename)
return len(data)

def benchmark(fn, *args, repeat=3):
times = []
for _ in range(repeat):
t0 = time.perf_counter()
fn(*args)
t1 = time.perf_counter()
times.append(t1 - t0)
print(f"{fn.__name__} -> {times}, avg={sum(times)/len(times):.4f}s")

if __name__ == "__main__":
benchmark(io_heavy, "tmp_test.txt", 2000, 2000)

3.8.6

raw-image

3.12.10

raw-image

純迴圈效能(for-loop + range)

這是最常見的 micro-benchmark,測試 Python 解譯器本身的迴圈執行速度。

import time

def loop_test(n=200_000_000):
s = 0
for i in range(n):
s += i
return s

if __name__ == "__main__":
t0 = time.perf_counter()
loop_test(200_000_000)
t1 = time.perf_counter()
print("Loop test:", t1 - t0, "s")

3.8.6

raw-image

3.10.12

raw-image

NumPyOpenCV

  • 自動產生接近2,0000,000(2e7)畫素的單通道灰階圖(或 RGB 可選)
  • 針對 NumPy 做矩陣乘法(用 sub-block 或隨機矩陣)、元素級運算、FFT 等(以大陣列為基礎)
  • 針對 OpenCV 做 GaussianBlur、Canny、Resize、findContours、connectedComponentsWithStats
  • 每個操作做 repeat 次,計算平均耗時並印出
  • 接受命令列參數:--pixels--channels--repeat
"""
benchmark_big_image.py

用途:針對大圖 (預設 20,000,000 pixels) 比較 NumPy / OpenCV 常見運算平均耗時。
範例:
python benchmark_big_image.py # 使用預設 (20M pixels, gray, repeat=10)
python benchmark_big_image.py --repeat 50 --channels 3
python benchmark_big_image.py --pixels 20000000 --channels 1 --repeat 100
"""

import time
import argparse
import math
import numpy as np
import cv2
import sys

def make_image(total_pixels=20_000_000, channels=1, dtype=np.uint8, seed=42):
"""產生接近 total_pixels 的影像 (channels=1 or 3).
回傳 numpy array, shape = (h, w, channels) (h, w) if channels==1
"""
np.random.seed(seed)
# 找最接近方形的寬高
side = int(math.sqrt(total_pixels))
h = side
w = int(total_pixels / h)
# 若 channels == 1, 回傳 (h,w);否則 (h,w,channels)
if channels == 1:
img = np.random.randint(0, 256, size=(h, w), dtype=dtype)
else:
img = np.random.randint(0, 256, size=(h, w, channels), dtype=dtype)
actual_pixels = img.shape[0] * img.shape[1]
print(f"Created image shape={img.shape}, actual_pixels={actual_pixels:,}")
return img

def timeit(fn, repeat=10):
times = []
for _ in range(repeat):
t0 = time.perf_counter()
fn()
t1 = time.perf_counter()
times.append(t1 - t0)
avg = sum(times) / len(times)
return avg, times

# ---------- NumPy tests ----------
def numpy_big_tests(img, repeat=10):
print("\n=== NumPy tests ===")
# For heavy numeric linear algebra we avoid creating huge random matrices each repeat.
# We'll create once and reuse.
# For matrix-multiply, we need 2D float matrices. If img is huge (single channel), convert to float and use smaller square matrices for matmul to keep memory sane.
h, w = img.shape[:2]
# build moderately sized float matrices derived from image but not too huge:
mat_size = min(2000, int(math.sqrt(h * w / 10_000))) # heuristic to avoid insane matmul sizes
A = np.random.rand(mat_size, mat_size).astype(np.float64)
B = np.random.rand(mat_size, mat_size).astype(np.float64)

def matmul_op():
# measure only dot product
_ = A.dot(B)
return None

avg_matmul, times_matmul = timeit(matmul_op, repeat=repeat)
print(f"Matrix multiply (size={mat_size}x{mat_size}) avg: {avg_matmul:.6f} s over {repeat} runs")

def element_wise_op():
# element-wise operations on the full image (convert to float)
x = img.astype(np.float32)
y = x * 1.234 + (x ** 2) * 0.0001
# reduce
_ = y.mean()
return None

avg_elem, _ = timeit(element_wise_op, repeat=repeat)
print(f"Element-wise ops on full image avg: {avg_elem:.6f} s over {repeat} runs")

def fft_op():
# FFT on a smaller block to be realistic (full 20M FFT is huge); use a central crop
ch, cw = min(1024, h), min(1024, w)
crop = img[:ch, :cw].astype(np.float32)
_ = np.fft.fft2(crop)
return None

avg_fft, _ = timeit(fft_op, repeat=repeat)
print(f"FFT on {min(1024,h)}x{min(1024,w)} crop avg: {avg_fft:.6f} s over {repeat} runs")

return {
"matmul_avg": avg_matmul,
"elementwise_avg": avg_elem,
"fft_avg": avg_fft
}

# ---------- OpenCV tests ----------
def opencv_big_tests(img, repeat=10):
print("\n=== OpenCV tests ===")
# ensure gray image for some ops
if img.ndim == 3:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
else:
gray = img

def gaussian_op():
_ = cv2.GaussianBlur(gray, (5, 5), 0)
return None

def canny_op():
_ = cv2.Canny(gray, 100, 200)
return None

def resize_op():
_ = cv2.resize(gray, (gray.shape[1] // 2, gray.shape[0] // 2))
return None

# prepare binary for contour/cc
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

def findcontours_op():
# note: OpenCV's return signature depends on version; ignore results
_ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return None

def connected_components_op():
_ = cv2.connectedComponentsWithStats(binary, connectivity=8)
return None

avg_gauss, _ = timeit(gaussian_op, repeat=repeat)
print(f"GaussianBlur avg: {avg_gauss:.6f} s over {repeat} runs")

avg_canny, _ = timeit(canny_op, repeat=repeat)
print(f"Canny avg: {avg_canny:.6f} s over {repeat} runs")

avg_resize, _ = timeit(resize_op, repeat=repeat)
print(f"Resize avg: {avg_resize:.6f} s over {repeat} runs")

avg_findcontours, _ = timeit(findcontours_op, repeat=repeat)
print(f"findContours avg: {avg_findcontours:.6f} s over {repeat} runs")

avg_conncomp, _ = timeit(connected_components_op, repeat=repeat)
print(f"connectedComponentsWithStats avg: {avg_conncomp:.6f} s over {repeat} runs")

return {
"gauss_avg": avg_gauss,
"canny_avg": avg_canny,
"resize_avg": avg_resize,
"findcontours_avg": avg_findcontours,
"conncomp_avg": avg_conncomp
}

def parse_args():
p = argparse.ArgumentParser()
p.add_argument("--pixels", type=int, default=20_000_000, help="total pixels (e.g. 20000000)")
p.add_argument("--channels", type=int, default=1, choices=[1,3], help="1=gray, 3=BGR")
p.add_argument("--repeat", type=int, default=10, help="repeat count for each op (default 10). Warning: large repeat * large image = long time")
p.add_argument("--seed", type=int, default=42, help="random seed")
return p.parse_args()

def main():
args = parse_args()
total_pixels = args.pixels
channels = args.channels
repeat = args.repeat

print(f"Benchmarking with total_pixels={total_pixels:,}, channels={channels}, repeat={repeat}")

img = make_image(total_pixels=total_pixels, channels=channels, seed=args.seed)

np_results = numpy_big_tests(img, repeat=repeat)
cv_results = opencv_big_tests(img, repeat=repeat)

# summary
print("\n=== Summary (averages in seconds) ===")
for k,v in {**np_results, **cv_results}.items():
print(f"{k}: {v:.6f}")

if __name__ == "__main__":
main()

3.8.6

raw-image

3.12.10

raw-image













留言
avatar-img
螃蟹_crab的沙龍
159會員
315內容數
本業是影像辨識軟體開發,閒暇時間進修AI相關內容,將學習到的內容寫成文章分享。 興趣是攝影,踏青,探索未知領域。 人生就是不斷的挑戰及自我認清,希望老了躺在床上不會後悔自己什麼都沒做。
螃蟹_crab的沙龍的其他內容
2025/09/04
1. 什麼是物件的記憶體位置? 在 Python 中,每個物件在記憶體中都有一個唯一的位置,這個位置可以用 id() 函式查詢。 這個 id 在 CPython(最常見的 Python 實作)中,實際上就是物件的記憶體地址。 a = [1, 2, 3] print(id(a))
2025/09/04
1. 什麼是物件的記憶體位置? 在 Python 中,每個物件在記憶體中都有一個唯一的位置,這個位置可以用 id() 函式查詢。 這個 id 在 CPython(最常見的 Python 實作)中,實際上就是物件的記憶體地址。 a = [1, 2, 3] print(id(a))
2025/04/25
在 Python 中,字典(dict)是非常常用的資料結構,用來儲存 key-value 配對。而在操作字典時,.setdefault() 是一個常被忽略但非常實用的方法。 這篇文章會完整介紹 .setdefault() 的語法、用途、與實務範例,幫助你更有效率地處理字典資料。 什麼是 .se
Thumbnail
2025/04/25
在 Python 中,字典(dict)是非常常用的資料結構,用來儲存 key-value 配對。而在操作字典時,.setdefault() 是一個常被忽略但非常實用的方法。 這篇文章會完整介紹 .setdefault() 的語法、用途、與實務範例,幫助你更有效率地處理字典資料。 什麼是 .se
Thumbnail
2025/01/30
Python 程式在電腦上的執行流程 當我們在電腦上執行 Python 程式時,主要經歷以下幾個步驟: 1. 編寫 Python 程式碼 開發者使用文字編輯器或 IDE(如 VS Code、PyCharm)撰寫 Python 程式,並將其存為 .py 檔案。 例如,一個簡單的 Python
Thumbnail
2025/01/30
Python 程式在電腦上的執行流程 當我們在電腦上執行 Python 程式時,主要經歷以下幾個步驟: 1. 編寫 Python 程式碼 開發者使用文字編輯器或 IDE(如 VS Code、PyCharm)撰寫 Python 程式,並將其存為 .py 檔案。 例如,一個簡單的 Python
Thumbnail
看更多
你可能也想看
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
在 vocus 與你一起探索內容、發掘靈感的路上,我們又將啟動新的冒險——vocus App 正式推出! 現在起,你可以在 iOS App Store 下載全新上架的 vocus App。 無論是在通勤路上、日常空檔,或一天結束後的放鬆時刻,都能自在沈浸在內容宇宙中。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
本文利用pyqt5,使用pyttsx3將QLineEdit(單行輸入框)的字串,轉成語音呈現出來。
Thumbnail
本文利用pyqt5,使用pyttsx3將QLineEdit(單行輸入框)的字串,轉成語音呈現出來。
Thumbnail
什麼是Python python是電腦程式語言的一種,如同python官方網站上的介紹 "Python是一種程式語,可讓你更快速地工作並更有效的整合系統"。簡單地說,就是你可用python這個程式語言去告訴電腦你想要作什麼,讓電腦來幫你完成你要作的事情。
Thumbnail
什麼是Python python是電腦程式語言的一種,如同python官方網站上的介紹 "Python是一種程式語,可讓你更快速地工作並更有效的整合系統"。簡單地說,就是你可用python這個程式語言去告訴電腦你想要作什麼,讓電腦來幫你完成你要作的事情。
Thumbnail
Python語法包括條件語句、迴圈、函數和變數的使用。條件語句如if、elif和else用於進行條件判斷,for和while是兩種主要的迴圈,def用於定義函數。變數可以被賦予數字或字符串,並可使用類型提示來指定變數的類型。註解可以是單行或多行,並可用於解釋函數或類的用途和作用。
Thumbnail
Python語法包括條件語句、迴圈、函數和變數的使用。條件語句如if、elif和else用於進行條件判斷,for和while是兩種主要的迴圈,def用於定義函數。變數可以被賦予數字或字符串,並可使用類型提示來指定變數的類型。註解可以是單行或多行,並可用於解釋函數或類的用途和作用。
Thumbnail
Python是一種易學且功能強大的程式語言,具有直譯、動態語法等特性,並擁有豐富的標準庫。它在各領域如Web開發、數據科學和人工智慧等得到廣泛應用,並被許多大公司如Google和Facebook等使用。Python還有強大的框架、豐富的交互機能、和龐大的社區。
Thumbnail
Python是一種易學且功能強大的程式語言,具有直譯、動態語法等特性,並擁有豐富的標準庫。它在各領域如Web開發、數據科學和人工智慧等得到廣泛應用,並被許多大公司如Google和Facebook等使用。Python還有強大的框架、豐富的交互機能、和龐大的社區。
Thumbnail
在實務上,若Python報錯時,若引入的套件越多伴隨的異常訊息會變得越來越複雜,看到一推密密麻麻的內容時,很多時候都想直接跳過。 本文將利用Traceback來讓異常訊息變得更好理解。
Thumbnail
在實務上,若Python報錯時,若引入的套件越多伴隨的異常訊息會變得越來越複雜,看到一推密密麻麻的內容時,很多時候都想直接跳過。 本文將利用Traceback來讓異常訊息變得更好理解。
Thumbnail
你知道IG是用Django開發的嗎? 正在學習或使用Django、Flask框架開發後端的你,是否也常在享受Python語法的舒適之餘,仍然煩惱著是否該學習效率更好的GO或Laravel。
Thumbnail
你知道IG是用Django開發的嗎? 正在學習或使用Django、Flask框架開發後端的你,是否也常在享受Python語法的舒適之餘,仍然煩惱著是否該學習效率更好的GO或Laravel。
Thumbnail
今天來介紹python的函式 函式在python中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
Thumbnail
今天來介紹python的函式 函式在python中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
Thumbnail
IDE 升級後出現了一樣的錯誤,手上程式碼沒有 pylint black-format 檢查上不了 gitlab,我又點開了那個很小很小的 x 符號,裡面 logs 提示的解決方式是升級..
Thumbnail
IDE 升級後出現了一樣的錯誤,手上程式碼沒有 pylint black-format 檢查上不了 gitlab,我又點開了那個很小很小的 x 符號,裡面 logs 提示的解決方式是升級..
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News