最近想试一下python调用c会不会更快,看了大佬的文章打算用ctypes尝试以下,记录以下过程和bugs。
环境:
系统:
版本 Windows 11 专业版
版本 22H2
安装日期 2023/3/25
操作系统版本 22621.1555
体验 Windows Feature Experience Pack 1000.22640.1000.0编译器:
MSVC cl.exe 用于 x64 的 Microsoft (R) C/C++ 优化编译器 19.35.32216.1 版
Python:
3.11.1
IDE:
Clion 2022.3.1
首先在项目目录创建cpp文件,gm.cpp:
int gm() {
return 1;
}
int add(int a, int b) {
return a + b;
}
在clion的终端执行cl,编译成dll文件:
cl /LD gm.cpp /o gm.dll
但是!
会报错:LINK : fatal error LNK1104: 无法打开文件“LIBCMT.lib”
google一搜,原来这些编译都是要在VS的终端中进行,于是打开 Visual Studio 2022 Developer Command Prompt v17.5.3
编译,成功!在项目目录得到了:
写段python,test.py,运行:
from ctypes import *
great_module = CDLL('./gm.dll')
print(great_module.gm(13))
add = great_module.add
print(add(41, 1))
嗯?又报错:
Traceback (most recent call last):
File "E:\works\c&c++\Cpython\test.py", line 4, in <module>
print(great_module.gm(13))
^^^^^^^^^^^^^^^
File "D:\Python311\Lib\ctypes\__init__.py", line 389, in __getattr__
func = self.__getitem__(name)
^^^^^^^^^^^^^^^^^^^^^^
File "D:\Python311\Lib\ctypes\__init__.py", line 394, in __getitem__
func = self._FuncPtr((name_or_ordinal, self))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: function 'gm' not found
为啥没找到函数gm?继续Google。。。
原来是要定义一下,声明为可以被找到的函数,gm.cpp改成:
extern "C" {
int gm();
int add(int a, int b);
}
int gm() {
return 1;
}
int add(int a, int b) {
return a + b;
}
再次编译,运行,还是报错?再搜,再改:
原来是MSVC的问题,它也要看个声明,那就加呗:
#ifdef _MSC_VER
#define DLL_EXPORT __declspec( dllexport )
#else
#define DLL_EXPORT
#endif
extern "C" {
DLL_EXPORT int gm();
DLL_EXPORT int add(int a, int b);
}
DLL_EXPORT int gm() {
return 1;
}
DLL_EXPORT int add(int a, int b) {
return a + b;
}
再次编译,运行,成功了!
测测速度:
from ctypes import *
great_module = CDLL('./gm.dll')
add = great_module.add
import time
t = time.perf_counter()
for i in range(10000000):
10000 + 20000
print(time.perf_counter() - t)
t = time.perf_counter()
for i in range(10000000):
add(10000, 20000)
print(time.perf_counter() - t)
为啥更慢呢?
把循环放到c的函数里试试,改gm.cpp:
#ifdef _MSC_VER
#define DLL_EXPORT __declspec( dllexport )
#else
#define DLL_EXPORT
#endif
extern "C" {
DLL_EXPORT int gm();
DLL_EXPORT int add(int a, int b);
DLL_EXPORT int for_add(int a, int b);
}
DLL_EXPORT int gm() {
return 1;
}
DLL_EXPORT int add(int a, int b) {
return a + b;
}
DLL_EXPORT int for_add(int a, int b) {
for (int i = 0; i < 10000000; ++i) {
a + b;
}
return a + b;
}
改test.py:
from ctypes import *
great_module = CDLL('./gm.dll')
for_add = great_module.for_add
import time
# t = time.perf_counter()
# print(time.perf_counter() - t)
t = time.perf_counter()
for i in range(10000000):
10000 + 20000
print(time.perf_counter() - t)
t = time.perf_counter()
for_add(10000,20000)
print(time.perf_counter() - t)
似乎这样c就快了很多:
应该是从dll中调用这一步骤要花费比较长的时间,减少调用次数就可以加速?