我们知道python由于全局解释器锁的存在,在多线程编程时,同时只能有一个线程进入解释器代码执行,无法发挥多核的能力,尤其在代码是CPU密集型的情况下,性能会很糟糕。

特别是大部分的深度学习项目,普遍使用python来实现,虽然深度学习主要的运算是神经网络的运算,一般在GPU执行,但是很情况下还会涉及一些搜索类的算法,如:viterbi算法,是设计CPU密集运算的。

解决该问题的思路是可以使用C语言扩展模块来实现CPU密集部分的代码,在调用C语言模块时可以释放全局解释器锁,使该部分代码可以支持多核并行。这样即发挥了python编程开发效率高的优势,又解决了部分需要并发来提升性能的问题。

python本身使用C语言实现,天然预留了实现C语言扩展模块的编程接口。然而使用原生接口来实现该扩展,比较麻烦,尤其在深学习项目中,涉及到numpy类型的参数传递,很繁琐。

我们在寻找方案过程中,找到了Cython这个项目。Cython除了充分支持python原生语言,还实现了一套扩展的语言,在扩展语言部分可以通过特殊变量的声明,提升一些代码效率。我们主要关注的是Cython使编写C扩展非常方便,特别是numpy参数的传递,全局解释器锁的释放。

下面列一个简单例子: 假如深度学项目中生成一个numpy类型二维数组:scores,第一维为时间序列,第二维位应用softmax后的多类概率分布。现在要使用viterbi搜索一累积概率最大路径。 用C实现一个搜索函数: void get_maxscore_path(float scores*, int time_len, int n_len, int path); 下面需要封装一个python模块供python代码调用。

cimport cython
cimport numpy as np
import numpy as np
@cython.boundscheck(False)
@cython.wraparound(False)
cpdef void warp_get_maxscore_path(float[:,:] scores, int time_len, int n_len, int[:] path) nogil:
with nogil:
return get_maxscore_path(&scores[0,0], time_len, n_len, &path[0])

使用setuptools编译python模块,即可在python代码中直调用,二维numpy数组直接转为C语言的二维数组。