为何通过ctypes调用C代码比直接运行C程序更快?
Python调用C函数比GCC编译的C可执行文件更快的原因
问题描述
我做了一项Python与C的性能对比测试:编写程序计数到10亿,通过ctypes让Python调用C函数,同时直接运行C程序。测试结果显示Python调用C函数的耗时(约0.29-0.305秒)反而比直接运行GCC编译的C程序(约0.51-0.55秒)更快,这与预期不符。换用Clang编译后结果恢复正常(C程序更快)。
测试代码
C代码count.c:
// count.c #include <stdio.h> long int count(long int n) { long int number = 0; for(long i = 0; i < n; i++) { number++; } return number; } int main() { printf("%ld\n", count(1000000000)); return 0; }
Python代码count.py:
# count.py from ctypes import CDLL so_file = "./count.so" count_func = CDLL(so_file).count print(count_func(1000_000_000))
环境信息
- GCC版本:gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
- Clang版本:Ubuntu clang version 14.0.0-1ubuntu1
- 系统:Ubuntu 22.04.1 LTS x86_64
原因分析
1. GCC对可执行文件与共享库的默认优化策略不一致
这是核心原因:
- 编译可执行文件时:GCC默认使用
-O0(无优化)等级,编译器保留完整调试信息,不对循环做任何优化——count函数里的循环会被逐次执行,完全没有常量折叠、循环展开等优化,执行效率极低。 - 编译共享库时:GCC会自动启用部分优化(比如
-O1级别的部分优化项),此时编译器能识别到number的最终值等于输入参数n,直接返回n跳过循环,执行速度大幅提升。
Python调用的是经过优化的共享库,而直接运行的是未优化的C可执行文件,因此出现Python调用更快的反直觉结果。
2. Clang的统一优化行为
Clang在编译可执行文件和共享库时,默认优化策略更统一,都会对这种无意义的循环做常量折叠优化,直接返回n。因此无论是直接运行Clang编译的C程序,还是Python调用其编译的共享库,速度都很快;且C程序因为没有Python调用的额外开销,表现会更优。
3. 验证方式
可以通过手动指定优化等级验证:
- 用GCC编译可执行文件时加
-O2优化:gcc -O2 count.c -o count_opt,此时直接运行该可执行文件的速度会和Python调用共享库相当甚至更快。 - 用GCC编译共享库时强制
-O0:gcc -shared -fPIC -O0 count.c -o count_noopt.so,此时Python调用该共享库的速度会变慢,与未优化的C可执行文件耗时接近。
内容的提问来源于stack exchange,提问作者Gray Hat
相关产品推荐
相关产品推荐

