python--用 OpenMP 并行多核加速 Python!
来源:互联网 发布:php沐辰的博客 编辑:程序博客网 时间:2024/05/22 01:39
转自:http://blog.csdn.net/gzlaiyonghao/article/details/6670128/
赖勇浩(http://laiyonghao.com)
注:
0、读懂这篇文章需要了解 OpenMP 基本用法。
1、读懂这篇文章需要了解 GIL 基本概念。
2、基本上是这篇的翻译:http://docs.cython.org/src/userguide/parallelism.html,标题是我自己取的,如有错漏、不明,敬请参详原文。
3、本篇不是使用 cython.parallel 的指南(或手册),仅作信息传播之用。
4、我之前翻译过一篇文章《OpenMP与C++:事半功倍地获得多线程的好处》有助于理解这篇文章,见:上(http://blog.csdn.net/lanphaday/article/details/1503817),下(http://blog.csdn.net/lanphaday/article/details/1507834)。
Cython 0.15 新增了 cython.parallel 模块,实现对原生并行编程的支持。现在只支持 OpenMP,以后会加入更多的后端支持。需要注意的是并行是运行在释放了 GIL 的环境下的。
cython.parallel.prange([start], stop[, step], nogil=False, schedule=None)
此函数并行循环,OpenMP 自动构建线程池,并根据指定的调度方案分派作业给这些线程。step 参数不可为 0,如果 nogil 参数为 true,那么这个循环就会被包装在一个 nogil 环境中。shedule 参数支持 static/dynamic/guided/auto/runtime 等 OpenMP 中定义的调度机制。
thread-locality 和 reduction 是从变量进来推断决定的。在 prange 块中被赋值的变量,会被看作 lastprivate,意思是这个变量的值会是最后一次迭代的值。如果对变量使用了原地操作符,那它会被看作 reduction,意思是每条线程都拷贝了一个私有变量,然后在循环结束后应用这个操作符,并赋值给原来的变量。索引变量总是 lastprivate,而在并行块中被赋值的变量都会被看作 private,而且在离开并行块后不可用,因为无法确定它的最后的值。(译注:对这两段理解不能的话,需要阅读 OpenMP 相关文档)。
下面是一个关于 reduction 的例子:
- from cython.parallel import prange, parallel, threadid
- cdef int i
- cdef int sum = 0
- for i in prange(n, nogil=True):
- sum += i
- print sum
from cython.parallel import prange, parallel, threadidcdef int icdef int sum = 0for i in prange(n, nogil=True): sum += iprint sum再来一个共享 numpy 数组的例子:
- from cython.parallel import *
- def func(np.ndarray[double] x, double alpha):
- cdef Py_ssize_t i
- for i in prange(x.shape[0]):
- x[i] = alpha * x[i]
from cython.parallel import *def func(np.ndarray[double] x, double alpha): cdef Py_ssize_t i for i in prange(x.shape[0]): x[i] = alpha * x[i]
cython.parallel.parallel()
可以在 with 语句中使用这个指令来实现代码序列的并行执行。这在为 prange 准备 thread-local 的缓冲区时非常有用。内含的 prange 将成为不并行的工作共享循环,所以一切在并行 section 中被赋值的变量在 prange 中也是 private。所有并行块中的 private 变量在离开并行块后都不可用。
thread-local 缓冲的例子:
- from cython.parallel import *
- from libc.stdlib cimport abort, malloc, free
- cdef Py_ssize_t idx, i, n = 100
- cdef int * local_buf
- cdef size_t size = 10
- with nogil, parallel():
- local_buf = <int *> malloc(sizeof(int) * size)
- if local_buf == NULL:
- abort()
- # populate our local buffer in a sequential loop
- for idx in range(size):
- local_buf[i] = i * 2
- # share the work using the thread-local buffer(s)
- for i in prange(n, schedule='guided'):
- func(local_buf)
- free(local_buf)
from cython.parallel import *from libc.stdlib cimport abort, malloc, freecdef Py_ssize_t idx, i, n = 100cdef int * local_bufcdef size_t size = 10with nogil, parallel(): local_buf = <int *> malloc(sizeof(int) * size) if local_buf == NULL: abort() # populate our local buffer in a sequential loop for idx in range(size): local_buf[i] = i * 2 # share the work using the thread-local buffer(s) for i in prange(n, schedule='guided'): func(local_buf) free(local_buf)以后 sections 将支持并行块,这样可以把 sections 的代码分配给多个线程执行。
cython.parallel.threadid()
返回线程 ID,对于 n 个线程,它们的 ID 范围是 [0, n)。
编译
要启用 OpenMP 支持,需要把 C 或 C++ 编译器的 OpenMP 开关打开,gcc 适用的 setup.py 如下:
- from distutils.core import setup
- from distutils.extension import Extension
- from Cython.Distutils import build_ext
- ext_module = Extension(
- "hello",
- ["hello.pyx"],
- extra_compile_args=['-fopenmp'],
- extra_link_args=['-fopenmp'],
- )
- setup(
- name = 'Hello world app',
- cmdclass = {'build_ext': build_ext},
- ext_modules = [ext_module],
- )
from distutils.core import setupfrom distutils.extension import Extensionfrom Cython.Distutils import build_extext_module = Extension( "hello", ["hello.pyx"], extra_compile_args=['-fopenmp'], extra_link_args=['-fopenmp'],)setup( name = 'Hello world app', cmdclass = {'build_ext': build_ext}, ext_modules = [ext_module],)
打断
nogil 模式下的并行的 with 和 prange 块支持 break、continue 和 return。此外,还能够在这些块中使用 with gil 块,也可以抛出异常。但是,因为使用了 OpenMP,不能跳出了事,最好还是退出程序。以 prange() 为例,在第一次 return、break 或抛出异常后,所有线程的每一次循环都会跳过。所以如果有多个值应当返回时该返回哪个值是没有定义的,因为迭代本身是没有特定的顺序的:
- from cython.parallel import prange
- cdef int func(Py_ssize_t n):
- cdef Py_ssize_t i
- for i in prange(n, nogil=True):
- if i == 8:
- with gil:
- raise Exception()
- elif i == 4:
- break
- elif i == 2:
- return i
from cython.parallel import prangecdef int func(Py_ssize_t n): cdef Py_ssize_t i for i in prange(n, nogil=True): if i == 8: with gil: raise Exception() elif i == 4: break elif i == 2: return i上例中到底是抛出异常,还是简单地 break 又或者返回 2,是没有定义的(不确定的)。
嵌套并行
因为 gcc 的一个 bug,现在嵌套并行被禁用掉了,不过,你可以在一个并行段中调用含有并行段的函数。
参考资料
[1] http://www.openmp.org/mp-documents/spec30.pdf
[2] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49897
- python--用 OpenMP 并行多核加速 Python!
- Cython 0.15,用 OpenMP 并行多核加速 Python!
- Cython 0.15,用 OpenMP 并行多核加速 Python!
- Parallel Python多核并行
- Python 多核并行计算
- 用openMP进行并行加速
- 用openMP进行并行加速
- 【Python】【并行计算】Python 多核并行计算
- OpenMP并行加速
- OpenMP多核并行框架概述
- 多核并行编程之OpenMP
- OpenMP多核并行化编程
- OpenMP并行加速笛卡尔乘积
- OpenMP系列--初次体验多核并行编程
- Qt使用openmp并行化加速程序
- 【Python - GPU】基于Python的GPU加速并行计算 -- pyCUDA
- 多核CPU上python多线程并行的一个假象
- 多核CPU上python多线程并行的一个假象
- linux音频驱动架构
- 运行hadoop example aggregatewordcount 出错的解决办法
- C语言 南阳理工ACM 223.小明的烦恼
- hdu 4705 Y/杭电2013年多校第十场1010 组合
- Deep Learning论文笔记之(二)Sparse Filtering稀疏滤波
- python--用 OpenMP 并行多核加速 Python!
- 这几天遇到的问题
- 20130822-STM8L101F3P6的PD0使用异常,尚未解决~!
- java如何获取客户端IP地址
- VC 后台服务程序(二)
- mfc动态添加垂直滚动条
- Settings源码分析
- Deep Learning论文笔记之(三)单层非监督学习网络分析
- FCKeditor图片上传 进度条不动