大数据文摘出品
编译:啤酒泡泡、宁静
Python是社区里最受喜爱的编程语言!它是目前为止最易使用的语言,因为它的代码短小精悍,符合人们的思维方式,也符合人们的阅读习惯。
但是你会经常听到有人吐槽Python,尤其是有些C语言的大牛吐槽Python速度慢。
他们说得没错,相比其他高级程序设计语言,如C语言来说,Python确实速度很慢,这主要是因为C语言更面向计算机底层,像一些单片机、电路板的设计都使用C语言,C语言和汇编语言之间的转换也更快,但是“every coin has two sides”,没有完美无缺的编程语言,C语言也有着其诸如代码量大、面向过程的一些缺点,如何让Python兼顾C语言速度方面的优点呢?
一个很形象的例子,就像上图展示的一样,赛车手需要兼顾汽车的发动引擎的内耗磨损,也要兼顾赛车的运行速度,那么,如何在两者之间取得平衡呢?
其实有很多可以提高运行速度的办法,比如:
利用多进程库来使用所有的CPU内核;
如果你正在使用NumPy,Pandas,或是Scikit-Learn库,那么可以使用Rapids来提高GPU的处理速度。
但是这只针对你的任务可以并行的情况,例如数据预处理、矩阵操作等,上述办法都很棒,可是如果你只使用纯Python语言,那该怎么办呢?再比如,你必须使用一个很大的for循环,而且因为数据必须被顺序处理导致你无法使用矩阵,在这种情况下,有没有办法提高Python本身的速度呢?
Cython就是用来加速纯Python代码的。
什么是Cython?
从本质上讲,Cython是Python和C/C++的桥梁。它允许你对Python代码稍作修改,然后把Python代码直接翻译成C语言代码。
你唯一需要修改Python代码的地方就是在每一个变量前面加上它的类型,通常,我们在Python里会这样声明变量:
x = 0.5
如果使用Cython,我们会给变量加上它的类型:
cdef float x = 0.5
这会告诉Cython,我们的变量是浮点型变量,这和我们在C语言中的操作一样。使用纯Python语言,变量的数据类型在赋值后被自动定义。Cython这种显式的变量声明方法使得Python代码转换成C代码成为可能,因为C语言要求变量的数据类型必须在声明变量时写出来。
使用pip安装Cython只需一行代码:
pip install cython
Cython的数据类型
使用Cython时,Cython提供两类类型,一类用于变量,一类用于函数。
对于变量,我们可以这样写(请注意,这些类型都来自C/C++):
def test(x):
y = 1
for i in range(1, x+1):
y *= i
return y
cpdef int test(int x):
cdef int y = 1
cdef int i
for i in range(1, x+1):
y *= i
return y
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules = cythonize('run_cython.pyx'))
python setup.py build_ext --inplace
/***the beginning part of the code***/
/* Generated by Cython 0.29.13 */
/* BEGIN: Cython Metadata
{
"distutils": {
"name": "run_cython",
"sources": [
"run_cython.pyx"
]
},
"module_name": "run_cython"
}
END: Cython Metadata */
/****the end part of the code***/
static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
Py_ssize_t ival;
PyObject *x;
if (likely(PyInt_CheckExact(b))) {
if (sizeof(Py_ssize_t) >= sizeof(long))
return PyInt_AS_LONG(b);
else
return PyInt_AsSsize_t(b);
}
if (likely(PyLong_CheckExact(b))) {
const digit* digits = ((PyLongObject*)b)->ob_digit;
const Py_ssize_t size = Py_SIZE(b);
if (likely(__Pyx_sst_abs(size) <= 1)) {
ival = likely(size) ? digits[0] : 0;
if (size == -1) ival = -ival;
return ival;
} else {
switch (size) {
case 2:
if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) {
return (Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
}
break;
case -2:
if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) {
return -(Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
}
break;
case 3:
if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) {
return (Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
}
break;
case -3:
if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) {
return -(Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
}
break;
case 4:
if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) {
return (Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
}
break;
case -4:
if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) {
return -(Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
}
break;
}
}
return PyLong_AsSsize_t(b);
}
x = PyNumber_Index(b);
if (!x) return -1;
ival = PyInt_AsSsize_t(x);
Py_DECREF(x);
return ival;
}
static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) {
return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False);
}
static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
return PyInt_FromSize_t(ival);
}
import run_python
import run_cython
import time
number = 10
start = time.time()
run_python.test(number)
end = time.time()
py_time = end - start
print("Python time = {}".format(py_time))
start = time.time()
run_cython.test(number)
end = time.time()
cy_time = end - start
print("Cython time = {}".format(cy_time))
print("Speedup = {}".format(py_time /
https://towardsdatascience.com/use-cython-to-get-more-than-30x-speedup-on-your-python-code-f6cb337919b6