简单CPython程序性能测量及从零构建与Python程序性能对比方法
从零开始编写简单CPython扩展并对比性能(以计算10!为例)
嘿,我来帮你把这个问题拆解清楚!很多刚接触CPython扩展的朋友都会疑惑怎么从零入手,刚好拿计算10的阶乘这个简单例子来一步步做,既能学会写扩展,又能直观看到性能差异。
先搞懂:什么是CPython扩展?
CPython是Python的官方实现,它本身就是用C写的。CPython扩展就是用C语言编写的模块,可以直接被Python导入调用——因为C是编译型语言,没有Python解释器的字节码解析、类型检查等额外开销,在计算密集型任务上能带来明显的性能提升。
步骤1:编写计算10!的CPython扩展
我们分三步完成这个扩展:
1. 编写核心C代码(factorial.c)
这个文件要包含Python的头文件,实现阶乘的C函数,再把它包装成Python可调用的接口,最后定义模块的初始化逻辑:
#include <Python.h> // 纯C实现的阶乘计算函数 long long factorial_c(int n) { long long result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result; } // 把C函数包装成Python可调用的函数 static PyObject* py_factorial(PyObject* self, PyObject* args) { int n; // 解析Python传入的参数(这里要求传入一个整数) if (!PyArg_ParseTuple(args, "i", &n)) { return NULL; // 参数解析失败时返回NULL,Python会抛出异常 } long long result = factorial_c(n); // 将C的long long类型转换为Python的整数对象 return PyLong_FromLongLong(result); } // 定义模块的方法列表 static PyMethodDef FactorialMethods[] = { {"factorial", py_factorial, METH_VARARGS, "Calculate the factorial of a number."}, {NULL, NULL, 0, NULL} // 列表结束标记 }; // 模块的结构体定义 static struct PyModuleDef factorialmodule = { PyModuleDef_HEAD_INIT, "factorial_ext", // 模块名(Python中导入时用这个名字) NULL, // 模块文档(可选) -1, // 保持全局状态(一般填-1即可) FactorialMethods }; // 模块初始化函数(必须叫PyInit_<模块名>) PyMODINIT_FUNC PyInit_factorial_ext(void) { return PyModule_Create(&factorialmodule); }
2. 编写编译配置文件(setup.py)
用setuptools来帮我们编译C代码生成Python可导入的模块:
from setuptools import setup, Extension # 定义扩展模块:模块名、源文件列表 module = Extension('factorial_ext', sources=['factorial.c']) setup( name='factorial_ext', version='1.0', description='A simple factorial calculation extension written in C', ext_modules=[module] )
3. 编译安装扩展
在终端执行以下命令,会在当前目录生成可直接导入的模块文件(Linux/macOS下是.so,Windows下是.pyd):
python setup.py build_ext --inplace
步骤2:对应的Python阶乘程序
我们写两个版本的Python实现作为对比:
# 版本1:纯Python循环实现 def factorial_py(n): result = 1 for i in range(1, n+1): result *= i return result # 版本2:Python标准库的math.factorial(本身也是C实现的,作为参考) import math
步骤3:性能测量与对比
用Python的timeit模块来测量性能——它会多次执行代码片段,减少单次执行的误差,结果更准确。
测量代码
import timeit import factorial_ext # 导入我们的CPython扩展 from factorial_py import factorial_py import math # 重复执行100万次,测量时间 ext_time = timeit.timeit(lambda: factorial_ext.factorial(10), number=1000000) py_time = timeit.timeit(lambda: factorial_py(10), number=1000000) math_time = timeit.timeit(lambda: math.factorial(10), number=1000000) # 打印结果 print(f"CPython扩展执行100万次10!的时间: {ext_time:.4f}秒") print(f"纯Python循环执行100万次10!的时间: {py_time:.4f}秒") print(f"math.factorial执行100万次10!的时间: {math_time:.4f}秒") # 计算性能提升倍数 py_speedup = py_time / ext_time math_speedup = math_time / ext_time print(f"\nCPython扩展比纯Python循环快 {py_speedup:.2f} 倍") print(f"CPython扩展比math.factorial快 {math_speedup:.2f} 倍(注:math.factorial本身也是优化过的C实现)")
预期结果
在普通机器上测试,你大概会看到这样的结果:
- CPython扩展:~0.02秒
- 纯Python循环:~0.15秒
- math.factorial:~0.02-0.03秒
也就是说,CPython扩展比纯Python循环快7-8倍,和标准库的C实现速度差不多(因为标准库做了更多边界处理和优化,可能略慢一点)。
为什么CPython扩展更快?
核心原因在于执行方式的差异:
- Python是解释型语言,每次循环都要做类型检查、字节码解析,还有Python对象的创建/销毁开销;
- C代码被编译成机器码直接执行,没有这些额外开销,计算效率拉满。
不过要注意:如果是IO密集型任务(比如读写文件、网络请求),CPython扩展的优势很小,因为瓶颈在IO等待,不在计算。
内容的提问来源于stack exchange,提问作者Abhinav Mishra
相关产品推荐
相关产品推荐

