我已经在C语言中实现了Python扩展,并发现在Python内部执行C函数比仅在C中执行C代码要快2倍。
但是为什么会更快?我本以为通过Python调用的纯C代码与直接从C中调用时性能应该完全相同。
这是我的实验:
- 纯C计算代码(简单的3for矩阵乘法) - 调用mmult()函数的纯C主函数 - 调用mmult()函数的Python扩展包装器 - 所有时间测量都在C代码内部进行
这里是我的结果:
纯C - 85us
Python扩展 - 36us
代码如下:
这里是我如何编译主要代码的过程:
这是我编译Python扩展的方法:
但是为什么会更快?我本以为通过Python调用的纯C代码与直接从C中调用时性能应该完全相同。
这是我的实验:
- 纯C计算代码(简单的3for矩阵乘法) - 调用mmult()函数的纯C主函数 - 调用mmult()函数的Python扩展包装器 - 所有时间测量都在C代码内部进行
这里是我的结果:
纯C - 85us
Python扩展 - 36us
代码如下:
#include "mmult.h"
void mmult(int32_t a[1024],int32_t b[1024],int32_t c[1024]) {
struct timeval t1, t2;
gettimeofday(&t1, NULL);
for(int i=0; i<32; i=i+1) {
for(int j=0; j<32; j=j+1) {
int32_t result=0;
for(int k=0; k<32; k=k+1) {
result+=a[i*32+k]*b[k*32+j];
}
c[i*32+j] = result;
}
}
gettimeofday(&t2, NULL);
double elapsedTime = (t2.tv_usec - t1.tv_usec) + (t2.tv_sec - t1.tv_sec)*1000000;
printf("elapsed time: %fus\n",elapsedTime);
}
--mmult.h-------
#include <stdint.h>
void mmult(int32_t a[1024],int32_t b[1024],int32_t c[1024]);
--main.cpp------
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include "mmult.h"
int main() {
int* a = (int*)malloc(sizeof(int)*1024);
int* b = (int*)malloc(sizeof(int)*1024);
int* c = (int*)malloc(sizeof(int)*1024);
for(int i=0; i<1024; i++) {
a[i]=i+1;
b[i]=i+1;
c[i]=0;
}
struct timeval t1, t2;
gettimeofday(&t1, NULL);
mmult(a,b,c);
gettimeofday(&t2, NULL);
double elapsedTime = (t2.tv_usec - t1.tv_usec) + (t2.tv_sec - t1.tv_sec)*1000000;
printf("elapsed time: %fus\n",elapsedTime);
free(a);
free(b);
free(c);
return 0;
}
这里是我如何编译主要代码的过程:
gcc -o main main.cpp mmult.cpp -O3
--wrapper.cpp-----
#include <Python.h>
#include <numpy/arrayobject.h>
#include "mmult.h"
static PyObject* mmult_wrapper(PyObject* self, PyObject* args) {
int32_t* a;
PyArrayObject* a_obj = NULL;
int32_t* b;
PyArrayObject* b_obj = NULL;
int32_t* c;
PyArrayObject* c_obj = NULL;
int res = PyArg_ParseTuple(args, "OOO", &a_obj, &b_obj, &c_obj);
if (!res)
return NULL;
a = (int32_t*) PyArray_DATA(a_obj);
b = (int32_t*) PyArray_DATA(b_obj);
c = (int32_t*) PyArray_DATA(c_obj);
/* call function */
mmult(a,b,c);
Py_RETURN_NONE;
}
/* define functions in module */
static PyMethodDef TheMethods[] = {
{"mmult_wrapper", mmult_wrapper, METH_VARARGS, "your c function"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef cModPyDem = {
PyModuleDef_HEAD_INIT,
"mmult", "Some documentation",
-1,
TheMethods
};
PyMODINIT_FUNC
PyInit_c_module(void) {
PyObject* retval = PyModule_Create(&cModPyDem);
import_array();
return retval;
}
--setup.py-----
import os
import numpy
from distutils.core import setup, Extension
cur = os.path.dirname(os.path.realpath(__file__))
c_module = Extension("c_module", sources=["wrapper.cpp","mmult.cpp"],include_dirs=[cur,numpy.get_include()])
setup(ext_modules=[c_module])
--code.py-----
import c_module
import time
import numpy as np
if __name__ == "__main__":
a = np.ndarray((32,32),dtype='int32',buffer=np.linspace(1,1024,1024,dtype='int32').reshape(32,32))
b = np.ndarray((32,32),dtype='int32',buffer=np.linspace(1,1024,1024,dtype='int32').reshape(32,32))
c = np.ndarray((32,32),dtype='int32',buffer=np.zeros((32,32),dtype='int32'))
c_module.mmult_wrapper(a,b,c)
这是我编译Python扩展的方法:
python3.6 setup_sw.py build_ext --inplace
更新
我已经更新了mmult.cpp代码,在内部运行3for循环1,000,000次。这导致非常相似的时间:
纯C - 27微秒
Python扩展 - 27微秒
int* a = (int*)malloc(sizeof(int)*1024);
-- 这个条目数对于有意义的基准测试来说是一个微不足道的数量。 - PaulMcKenzie