Cython - 運算與溢位
今天在使用 Cython 時踩到一個小雷,主要是因為 Python 是為了科學計算而生所以能處理很大的數字。 使用 Cython 時若將變數轉成 C 的型別可能會有溢位的情形。
1. Source code:
建立一個 frac.pyx 加入下面兩個 functions,一個是 C 版本另一個是 Python 版本。
cimport cython @cython.infer_types(True) cpdef cp_fract(int n): res = 1 for i in range(1, n+1): res *= i return res def py_fract(n): res = 1 for i in range(1, n+1): res *= i return res
建立一個 setup.py 用來 build Cython。
from distutils.core import setup from Cython.Build import cythonize setup(ext_modules=cythonize("frac.pyx"))
寫一個簡單的 makefile 來記編譯的指令,make 後就能在一般 Python 檔 import frac 模組。
build: frac.pyx python3 setup.py build_ext --inplace
2. 時間測試:
簡單在不溢位的情形下測試,f 是乘積的次數 n 是計算的次數。
def test_speed(f=20, n=100000): print(f"Python value: {py_frac(f)}") start = time() for _ in range(n): py_frac(f) end = time() print(f"python takes: {end-start} s") print(f"C value: {cp_frac(f)}") start = time() for _ in range(n): cp_frac(f) end = time() print(f"C takes: {end-start} s")
執行結果如下。可以發現 Python 跟 C 計算的值是一樣的但時間大約差 10 倍。
Python value: 2432902008176640000 python takes: 0.08506226539611816 s C value: 2432902008176640000 C takes: 0.008076906204223633 s
3. 溢位測試:
我們把乘積的次數增加進行溢位測試。
def test_overflow(f=40): print(f"Python value: {py_frac(f)}") print(f"C value: {cp_frac(f)}")
執行結果如下。可以發現 Python 的 object 可以儲存大的數字但 C 會有溢位的情形。
Python value: 815915283247897734345611269596115894272000000000 C value: -70609262346240000
4. 小結論:
使用 cython.infer_types 時要小心將 Python object 轉換成 C 時的溢位情形。
留言
張貼留言