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 = [("测试方法", "耗费时间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)

结果:

使用cython 而未对python代码做任何改动,就获得了数倍的加速效果

%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结果

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不改变任何代码

%%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

出乎意料,竟然最慢, 有可能是因为vector太慢的原因

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

总结

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加了类型注释之后,速度也几乎可以达到c的水平
  • cppyy这种动态的模块编译方式,性能有损失,当然也可能是因为使用了std::vector
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容