[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
留言分享你的想法!
avatar-img
螃蟹_crab的沙龍
154會員
302內容數
本業是影像辨識軟體開發,閒暇時間進修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
雙11於許多人而言,不只是單純的折扣狂歡,更是行事曆裡預定的,對美好生活的憧憬。 錢錢沒有不見,它變成了快樂,跟讓臥房、辦公桌、每天早晨的咖啡香升級的樣子! 這次格編突擊辦公室,也邀請 vocus「野格團」創作者分享掀開蝦皮購物車的簾幕,「加入購物車」的瞬間,藏著哪些靈感,或是對美好生活的想像?
Thumbnail
雙11於許多人而言,不只是單純的折扣狂歡,更是行事曆裡預定的,對美好生活的憧憬。 錢錢沒有不見,它變成了快樂,跟讓臥房、辦公桌、每天早晨的咖啡香升級的樣子! 這次格編突擊辦公室,也邀請 vocus「野格團」創作者分享掀開蝦皮購物車的簾幕,「加入購物車」的瞬間,藏著哪些靈感,或是對美好生活的想像?
Thumbnail
雙11購物節準備開跑,蝦皮推出超多優惠,與你分享實際入手的收納好物,包括貨櫃收納箱、真空收納袋、可站立筆袋等,並分享如何利用蝦皮分潤計畫,一邊購物一邊賺取額外收入,讓你買得開心、賺得也開心!
Thumbnail
雙11購物節準備開跑,蝦皮推出超多優惠,與你分享實際入手的收納好物,包括貨櫃收納箱、真空收納袋、可站立筆袋等,並分享如何利用蝦皮分潤計畫,一邊購物一邊賺取額外收入,讓你買得開心、賺得也開心!
Thumbnail
分享個人在新家裝潢後,精選 5 款蝦皮上的實用家居好物,包含客製化層架、MIT 地毯、沙發邊桌、分類垃圾桶及寵物碗架,從尺寸、功能到價格都符合需求,並提供詳細開箱心得與購買建議。
Thumbnail
分享個人在新家裝潢後,精選 5 款蝦皮上的實用家居好物,包含客製化層架、MIT 地毯、沙發邊桌、分類垃圾桶及寵物碗架,從尺寸、功能到價格都符合需求,並提供詳細開箱心得與購買建議。
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中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News