前言
使用c++写好的库,用python调用,速度怎么样?
C++ 库与 Python 结合使用的常见的方式
Cython
是一个强大的工具,可以将 Python 代码编译为 C 或 C++ 扩展模块,从而显著提高性能。下面是Cython
的使用示例。
1、准备工作
1.1 安装 Cython
确保你已经安装了 Cython:
pip install cython
1.2 示例代码结构
- 创建C++ 文件
math.cpp
和头文件math.h
。 - 创建
math_cython.pyx
,用于定义 Python 和 C++ 的接口。 - 创建
setup.py
文件,用于编译 Cython 和 C++ 代码。 - 创建
test.py
文件,用于测试。
Cython_Demo
|-- math.cpp
|-- math.h
|-- math_cython.pyx
|-- setup.py
`-- test.py
2、代码示例
2.1 math.h
#ifndef MATH_H
#define MATH_H
int add(int a,int b);
#endif //MATH_H
2.2 math.cpp
#include "math.h"
int add(int a,int b){
return a+b;
}
2.3 math_cython.pyx
#定义外部C++函数
cdef extern from "math.h":
int add(int a,int b)
#封装成python可调用函数
def py_add(int a, int b):
return add(a,b)
def py_add_loop(int interations, int a, int b):
cdef int i
cdef int result = 0
for i in range(interations):
result += add(a,b)
return result
2.4 setup.py
from setuptools import setup,Extension
from Cython.Build import cythonize
# 定义扩展模块
ext_modules = [
Extension(
name = "math_cython", #模块名城
sources = ["math_cython.pyx","math.cpp"], #包含Cython和C++文件
language="c++", #指定语言为 C++
)
]
# 编译扩展模块
setup(
name="math_cython",
ext_modules=cythonize(ext_modules, compiler_directives={'language_level': "3", 'boundscheck': False, 'wraparound': False}),
)
2.5 test.py
import timeit
import math_cython
# 纯 Python 版本
def py_add(a, b):
return a + b
def test_python():
result = 0
for _ in range(1000000):
result +=py_add(3, 4)
# print(f"{test_python.__name__}:{result}")
# Cython 版本
def test_cython():
result = 0
for _ in range(1000000):
result +=math_cython.py_add(3, 4)
# print(f"{test_cython.__name__}:{result}")
def test_cython_loop():
result = 0
result = math_cython.py_add_loop(1000000,3,4)
# print(f"{test_cython_loop.__name__}:{result}")
print("Python:",'\t', timeit.timeit(test_python, number=10))
print("Cython:",'\t',timeit.timeit(test_cython, number=10))
print("Cython loop:",'\t',timeit.timeit(test_cython_loop, number=10))
3、编译测试
3.1 编译扩展模块
运行以下命令编译扩展模块:
python setup.py build_ext --inplace
build_ext
: 这是 setuptools 中用于构建 Python 扩展模块(通常是由 C 或 C++ 代码编写的模块)的命令。--inplace
: 此选项告诉build_ext
命令将编译后的扩展模块直接放置在当前包目录中(即与源代码相同的目录),而不是放置在默认的构建目录下(通常是项目根目录下的build/
文件夹)。
扩展模块编译成功后的项目结构如下,math_cython.cpython-312-x86_64-linux-gnu.so
就是test.py
中import math_cython
所用到的库:
./Cython_Demo/
|-- build
| |-- lib.linux-x86_64-cpython-312
| | `-- math_cython.cpython-312-x86_64-linux-gnu.so
| `-- temp.linux-x86_64-cpython-312
| |-- math.o
| `-- math_cython.o
|-- math.cpp
|-- math.h
|-- math_cython.cpp
|-- math_cython.cpython-312-x86_64-linux-gnu.so
|-- math_cython.pyx
|-- setup.py
`-- test.py
3.2 性能测试
python test.py
Python: 0.41646847798256204
Cython: 0.4275268409983255
Cython loop: 0.011464812007034197
可以看出,纯python进行1000000次加法耗时和使用Cython基本一致,但是将1000000循环逻辑移到 Cython 中的Cython loop,速度提升非常大。
4、注意事项
Cython 的性能取决于你是否充分利用了它的特性。通过以下改进,你可以显著提高 Cython 的性能。其中,避免频繁调用 Python-C 接口是提升性能最明显的方式。
4.1 检查 Cython 代码中的类型声明
Cython 的性能优势主要来自于静态类型声明和编译优化。如果你的 Cython 函数中没有明确声明变量类型,Cython 会退化为普通的 Python 代码,性能提升有限。
4.2 启用 Cython 编译器优化
在编译 Cython 代码时,可以通过设置编译选项来启用更多的优化。
setup(
name="math_cython",
ext_modules=cythonize(ext_modules, compiler_directives={'language_level': "3", 'boundscheck': False, 'wraparound': False}),
)
- 优化选项:
boundscheck=False
: 禁用数组边界检查。wraparound=False
: 禁用负索引支持。- 这些选项可以减少运行时开销,提高性能。
4.3 避免频繁调用 Python-C 接口
每次调用 Cython 函数时,Python 和 C 之间会有一定的交互开销。如果调用次数过多(如循环中调用 100 万次),这种开销会累积。
# 在 Cython 中实现循环逻辑
def py_add_loop(int iterations, int a, int b):
cdef int i
for i in range(iterations):
add(a, b)