数组运算加速是至关科学计算重要的领域,本节我们以一个简单函数为例,使用C语言为python数组加速。

一、Cython

本函数为一维数组修剪最大最小值

version1

@cython.boundscheck(False)
@cython.wraparound(False)
cpdef clip(double[:] a, double min, double max, double[:] out):
'''
Clip the values in a to be between min and max. Result in out
'''
if min > max:
raise ValueError("min must be <= max")
if a.shape[0] != out.shape[0]:
raise ValueError("input and output arrays must be the same size")
for i in range(a.shape[0]):
if a[i] < min:
out[i] = min
elif a[i] > max:
out[i] = max
else:
out[i] = a[i]

利用Cython类型的内存视图,极大的简化了数组的操作。

  • cpdef clip() 声明了 clip() 同时为C级别函数以及Python级别函数。 在Cython中,这个是很重要的,因为它表示此函数调用要比其他Cython函数更加高效 (比如你想在另外一个不同的Cython函数中调用clip())。
  • 类型参数 double[:] adouble[:] out 声明这些参数为一维的双精度数组。 作为输入,它们会访问任何实现了内存视图接口的数组对象,这个在PEP 3118有详细定义。 包括了NumPy中的数组和内置的array库。
  • clip() 定义之前的两个装饰器可以优化下性能:
    • @cython.boundscheck(False) 省去了所有的数组越界检查, 当你知道下标访问不会越界的时候可以使用它
    • @cython.wraparound(False) 消除了相对数组尾部的负数下标的处理(类似Python列表)

version2_条件表达式

任何时候处理数组时,研究并改善底层算法同样可以极大的提示性能

@cython.boundscheck(False)
@cython.wraparound(False)
cpdef clip(double[:] a, double min, double max, double[:] out):
if min > max:
raise ValueError("min must be <= max")
if a.shape[0] != out.shape[0]:
raise ValueError("input and output arrays must be the same size")
for i in range(a.shape[0]):
out[i] = (a[i] if a[i] < max else max) if a[i] > min else min

version3_释放GIL

释放GIL,这样多个线程能并行运行,要这样做的话,需要修改代码,使用 with nogil:

@cython.boundscheck(False)
@cython.wraparound(False)
cpdef clip(double[:] a, double min, double max, double[:] out):
if min > max:
raise ValueError("min must be <= max")
if a.shape[0] != out.shape[0]:
raise ValueError("input and output arrays must be the same size")
with nogil:
for i in range(a.shape[0]):
out[i] = (a[i] if a[i] < max else max) if a[i] > min else min

编写setup.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext ext_modules = [
Extension('sample',
['sample.pyx'])
] setup(
name = 'Sample app',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules
)

使用 python3 setup.py build_ext --inplace 来构建它

效率测试示意如下,

>>> import sample
>>> import numpy
>>> b = numpy.random.uniform(-10,10,size=1000000)
>>> c = numpy.zeros_like(b)
>>> import timeit
>>> timeit.timeit('numpy.clip(b,-5,5,c)','from __main__ import b,c,numpy',number=1000)
>>> timeit.timeit('sample.clip(b,-5,5,c)','from __main__ import b,c,sample',
... number=1000)

其中使用numpy自己的clip对比试验,

2.6287411409430206  # numpy
2.8034782900940627 # v1
2.7247575907967985 # v2
2.6071253868285567 # v3

版本三近似于numpy的实现效果,其他版本差一些(每次试验结果都会略有差异,这里只是粗略的比较一下)。

二维数组处理版本参考:

@cython.boundscheck(False)
@cython.wraparound(False)
cpdef clip2d(double[:,:] a, double min, double max, double[:,:] out):
if min > max:
raise ValueError("min must be <= max")
for n in range(a.ndim):
if a.shape[n] != out.shape[n]:
raise TypeError("a and out have different shapes")
for i in range(a.shape[0]):
for j in range(a.shape[1]):
if a[i,j] < min:
out[i,j] = min
elif a[i,j] > max:
out[i,j] = max
else:
out[i,j] = a[i,j]

二、自己写接口

点击进入项目

sample.c中添加

/* n:longth of array */
void clip(double *a, int n, double min, double max, double *out) {
double x;
for (; n >= 0; n--, a++, out++) {
x = *a; *out = x > max ? max : (x < min ? min : x);
}
}

pysample.c中添加

// void clip(double *a, int n, double min, double max, double *out);
static PyObject *py_clip(PyObject *self, PyObject *args){
PyObject *a, *out;
int min, max;
if(!PyArg_ParseTuple(args, "OiiO", &a, &min, &max, &out)){ //py数组对象暂记
return NULL;
} // printf("%i, %i\n", min, max);
Py_buffer view_a, view_out; //py数组对象接收对象
if (PyObject_GetBuffer(a, &view_a,
PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) {
return NULL;
}
if (PyObject_GetBuffer(out, &view_out,
PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) {
return NULL;
}
clip(view_a.buf, view_a.shape[0], min, max, view_out.buf);
PyBuffer_Release(&view_a);
PyBuffer_Release(&view_out);
return Py_BuildValue("");
}

函数登记处添加

{"clip", py_clip, METH_VARARGS, "clip array"},

则可,实际测试发现,手动接口效率也很高,和numpy同一水平。

『Python CoolBook』Cython_高效数组操作的更多相关文章

  1. 『Python CoolBook』Cython

    github地址 使用Cython导入库的话,需要一下几个文件: .c:C函数源码 .h:C函数头 .pxd:Cython函数头 .pyx:包装函数 setup.py:python 本节示例.c和.h ...

  2. 『Python CoolBook』使用ctypes访问C代码_下_demo进阶

    点击进入项目 这一次我们尝试一下略微复杂的c程序. 一.C程序 头文件: #ifndef __SAMPLE_H__ #define __SAMPLE_H__ #include <math.h&g ...

  3. 『Python CoolBook』C扩展库_其三_简单数组操作

    点击进入项目 这里的数组要点在于: 数组结构,array.array或者numpy.array 本篇的数组仅限一维,不过基础的C数组也是一维 一.分块讲解 源函数 /* Average values ...

  4. 『Python CoolBook』C扩展库_其四_结构体操作与Capsule

    点击进入项目 一.Python生成C语言结构体 C语言中的结构体传给Python时会被封装为胶囊(Capsule), 我们想要一个如下结构体进行运算,则需要Python传入x.y两个浮点数, type ...

  5. 『Python CoolBook』C扩展库_其六_线程

    GIL操作 想让C扩展代码和Python解释器中的其他进程一起正确的执行, 那么你就需要去释放并重新获取全局解释器锁(GIL). 在Python接口封装中去释放并重新获取全局解释器锁(GIL),此时本 ...

  6. 『Python CoolBook』C扩展库_其五_C语言层面Python库之间调用API

    点击进入项目 一.C层面模块添加API 我们仍然操作如下结构体, #include <math.h> typedef struct Point { double x,y; } Point; ...

  7. 『Python CoolBook』C扩展库_其六_从C语言中调用Python代码

    点击进入项目 一.C语言运行pyfun的PyObject对象 思路是在C语言中提供实参,传给python函数: 获取py函数对象(PyObject),函数参数(C类型) 获取GIL(PyGILStat ...

  8. 『Python CoolBook』数据结构和算法_字典比较&字典和集合

    一.字典元素排序 dict.keys(),dict.values(),dict.items() 结合max.min.sorted.zip进行排序是个很好的办法,另外注意不使用zip时,字典的lambd ...

  9. 『Python CoolBook』使用ctypes访问C代码_上_用法讲解

    一.动态库文件生成 源文件hello.c #include "hello.h" #include <stdio.h> void hello(const char *na ...

随机推荐

  1. spark-sql集合的“条件过滤”,“合并”,“动态类型映射DataFrame”,“存储”

    List<String> basicList = new ArrayList<String>(); basicList.add("{\"name\" ...

  2. QSS独门秘籍:subcontrol

    QSS是C++ Qt中的界面美化神器,其语法和CSS区别不大,但是QSS有一个独有的功能——subcontrol,这是CSS所没有的,一个widget往往由多个子部件构成,利用subcontrol可以 ...

  3. java框架之SpringBoot(10)-启动流程及自定义starter

    启动流程 直接从 SpringBoot 程序入口的 run 方法看起: public static ConfigurableApplicationContext run(Object source, ...

  4. iptables精通

    前提基础: 当主机收到一个数据包后,数据包先在内核空间中处理,若发现目的地址是自身,则传到用户空间中交给对应的应用程序处理,若发现目的不是自身,则会将包丢弃或进行转发. iptables实现防火墙功能 ...

  5. DAX/PowerBI系列 - 累计总计(Cumulative Total)

    DAX/PowerBI系列 - 累计总计(Cumulative Total) 2017/07/23 更新:B列公式(见最后) 难度: ★★☆☆☆(2星) 适用: ★★☆☆☆(2星) 概况: 这个模式普 ...

  6. JAVA微信支付代码(WeChatPay.java 才是调用类)

    微信官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html MD5Util.java package weixin; import java.se ...

  7. Vue使用Typescript开发编译时提示“ERROR in ./src/main.ts Module build failed: TypeError: Cannot read property 'afterCompile' of undefined”的解决方法

    使用Typescript开发Vue,一切准备就绪.但npm start 时,提示“ ERROR in ./src/main.tsModule build failed: TypeError: Cann ...

  8. 实验:Oracle单节点RAC添加节点

    环境:RHEL 6.5 + Oracle 11.2.0.4 单节点RAC 需求:单节点RAC添加新节点 1.添加节点前的准备工作 2.正式添加节点 3.其他配置工作 1.添加节点前的准备工作 参考Or ...

  9. #WEB安全基础 : HTTP协议 | 0x4 各种协议与HTTP协议的关系(一个报文的旅行)

    报文是怎么旅行的呢? 在网络中有很多引路人,如HTTP协议,IP协议.TCP协议.DNS协议以及ARP协议. 请看下图,演绎一个报文的旅程 这就是一个报文的完整请求过程,请加以理解并记忆 //本系列教 ...

  10. spring boot 配置多数据源

    https://www.jianshu.com/p/b2e53a2521fc