加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

在Python中运行C扩展比普通C更快

发布时间:2020-12-20 12:04:26 所属栏目:Python 来源:网络整理
导读:我在C中实现了一个 Python扩展,发现在 Python中执行C函数要比从C main执行C代码快2倍. 但为什么这会更快?我希望普通的C在从Python调用时与从C调用时的性能完全相同. 这是我的实验: 普通C计算代码(简单3用于矩阵 – 矩阵乘法) Plain C main函数调用mmult()
我在C中实现了一个 Python扩展,发现在 Python中执行C函数要比从C main执行C代码快2倍.

但为什么这会更快?我希望普通的C在从Python调用时与从C调用时的性能完全相同.

这是我的实验:

>普通C计算代码(简单3用于矩阵 – 矩阵乘法)
> Plain C main函数调用mmult()函数
> Python扩展包装器来调用mmult()函数
>所有时间都完全在C代码中发生

这是我的结果:

纯C – 85us

Python扩展 – 36us

继承我的代码:

–mmult.cpp ———-

#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: %fusn",elapsedTime);

}

–mmult.h ——-

#include <stdint.h>

void mmult(int32_t a[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,NULL);
  mmult(a,b,c);
  gettimeofday(&t2,elapsedTime);
  free(a);
  free(b);
  free(c);

  return 0;
}

以下是我如何编译main:

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,c);

   Py_RETURN_NONE;
}

/*  define functions in module */
static PyMethodDef TheMethods[] = {
   {"mmult_wrapper",mmult_wrapper,METH_VARARGS,"your c function"},{NULL,NULL,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,dtype='int32').reshape(32,32))
    b = np.ndarray((32,32))
    c = np.ndarray((32,buffer=np.zeros((32,dtype='int32'))

    c_module.mmult_wrapper(a,c)

下面是我如何编译Python扩展:

python3.6 setup_sw.py build_ext --inplace

UPDATE

我已经更新了mmult.cpp代码,以便在内部运行3到1,000,000次迭代.这导致非常相似的时间:

纯C – 27us

Python扩展 – 27us

解决方法

85微秒的延迟太小,无法可靠且重复地测量.例如,CPU cache效果(或 context switches或 paging)可能支配计算时间(并改变它以使该时间无意义).

(我猜你在Linux / x86-64上)

根据经验,尝试至少持续运行半秒钟,并重复基准测试几次.您也可以使用time(1)进行测量.

另请参阅time(7).有几种时间概念(经过“实际”时间,单调时间,进程CPU时间,线程CPU时间等).您可以考虑使用clock(3)或clock_gettime(2)来测量时间.

顺便说一句,您可以使用更新版本的GCC(2017年11月,GCC7和几周GCC8)进行编译,并且您希望使用gcc -march = native -O3进行编译以进行基准测试.尝试其他optimization options和调整.您也可以尝试其他编译器,例如Clang/LLVM.

另请参阅this对相关问题的回答(关于并行化).可能numpy软件包使用(内部)类似技术(在Python GIL之外),因此可能比C中的初始顺序矩阵乘法代码更快.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读