CPPYY PART III 和pypy/cython/numba的性能對比

CPPYY 與 Cython / Pypy / numba的速度對比 PART III

以vec_dot 為例,測試以下幾種不同方法的性能

1. 純python

def vec_dot(vec1, vec2):
    if len(vec1) != len(vec2):
        return []
    size = len(vec1)
    ret = []
    for i in range(size):
        ret.append(vec1[i] * vec2[i])
    return ret
import random
import numpy as np
import array
vec1_arr = np.random.rand(1000)
vec2_arr = np.random.rand(1000)
vec1 = vec1_arr.tolist()
vec2 = vec2_arr.tolist()
vec1_parr = array.array("d", vec1)
vec2_parr = array.array("d", vec2)
result = [("測試方法", "耗費(fèi)時間us")]
%timeit vec_dot(vec1, vec2)
111 μs ± 1.02 μs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
result.append(("純python", 111))
# 使用np.ndarray 有額外的開銷
%timeit vec_dot(vec1_arr, vec2_arr)
420 μs ± 28.6 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit vec_dot(vec1_parr, vec2_parr)
164 μs ± 12 μs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

2. Cython

%load_ext cython
%%cython --cplus
import numpy as np
import cython

# same code as python version
def vec_dot_cython(vec1, vec2):
    if len(vec1) != len(vec2):
        return []
    size = len(vec1)
    ret = []
    for i in range(size):
        ret.append(vec1[i] * vec2[i])
    return ret

# cython 的memoryview 可以用numpy.array 來初始化,或者使用python的memoryview/array.array對象
@cython.boundscheck(False)
@cython.wraparound(False)
cdef  _vec_dot_cython2(double[:] vec1, double[:] vec2, double[:] ret):
    cdef int size = vec1.size, i
    if size == vec2.size:
        for i in range(size):
            ret[i] = vec1[i] * vec2[i]

def vec_dot_cython2(vec1, vec2, ret):
    _vec_dot_cython2(vec1, vec2, ret)

結(jié)果:

使用cython 而未對python代碼做任何改動,就獲得了數(shù)倍的加速效果

%timeit vec_dot_cython(vec1, vec2)
34.4 μs ± 606 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
result.append(("cython沒有類型注釋", 34.4))

添加變量類型注釋之后的cython結(jié)果

ret = np.empty_like(vec1_arr)
%timeit vec_dot_cython2(vec1_arr, vec2_arr, ret)
3.54 μs ± 99.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
result.append(("cython有類型注釋", 3.54))

3. Pypy

使用pypy不改變?nèi)魏未a

%%pypy
def vec_dot(vec1, vec2):
    if len(vec1) != len(vec2):
        return []
    size = len(vec1)
    ret = []
    for i in range(size):
        ret.append(vec1[i] * vec2[i])
    return ret

from random import random
from timeit import Timer
vec1 = [random() for _ in range(1000)]
vec2 = [random() for _ in range(1000)]
vec_dot(vec1, vec2)
timer1 = Timer("vec_dot(vec1, vec2)", "from __main__ import vec_dot, vec1, vec2")
print("pypy3: %s us" % (1000 * timer1.timeit(1000)))
pypy3: 12.6938 us
result.append(("pypy", 12.68))

4. numba

numba 簡直要超神啊

import numba

@numba.jit(nopython=True)
def vec_dot_numba(vec1, vec2, ret):
    if vec1.shape[0] != vec2.shape[0]:
        return 
    size = vec1.shape[0]
    for i in range(size):
        ret[i] = vec1[i] * vec2[i]
%timeit vec_dot_numba(vec1_arr, vec2_arr, ret)
887 ns ± 13.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
result.append(("numbaJIT", 0.887))

5. cppyy

出乎意料,竟然最慢, 有可能是因?yàn)関ector太慢的原因

import cppyy
cppyy.cppdef("""
void vec_dot_cppyy(const std::vector<double> &vec1, const std::vector<double> &vec2, std::vector<double> &ret){
    if (vec1.size() != vec2.size()) return;
    int size = vec1.size();
    for (int i=0;i<size;i++){
        ret[i] = vec1[i] * vec2[i];
    }
}
""")
True
vec_dot_cppyy = cppyy.gbl.vec_dot_cppyy
Vector = cppyy.gbl.std.vector
vec1_cpp = Vector[float](vec1)
vec2_cpp = Vector[float](vec2)
%timeit vec_dot_cppyy(vec1, vec2)
140 μs ± 35.7 μs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
result.append(("cppyy c++ vector", 140))

6. 看看numpy的速度

%timeit np.dot(vec1_arr, vec2_arr)
1.23 μs ± 8.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
result.append(("numpy", 1.23))

總結(jié)

import pandas as pd
df = pd.DataFrame(result[1:], columns=result[0])
df.sort_values(by=result[0][1], inplace=True)
df.index = range(1, 8)
df
不同方法的耗時
  • 可以看出來numbaJIT和numpy是一個量級的,速度幾乎差不太多
  • cython即便沒有類型注釋,使用原模原樣的python代碼也可以加速很多
  • cython加了類型注釋之后,速度也幾乎可以達(dá)到c的水平
  • cppyy這種動態(tài)的模塊編譯方式,性能有損失,當(dāng)然也可能是因?yàn)槭褂昧藄td::vector
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容