一、对于python的基础介绍
Python是一种高效的动态编程语言,广泛用于科学,工程和数据分析应用程序。。影响python普及的因素有很多,包括干净,富有表现力的语法和标准数据结构,全面的“电池包含”标准库,优秀的文档,广泛的图书馆和工具生态系统,专业支持的可用性以及大而开放社区。但也许最重要的是,像Python这样的动态类型化解释语言能够实现高效率。Python灵活灵活,使其成为快速原型设计的理想语言,同时也是构建完整系统的理想语言。
但是Python的最大优势也可能是它最大的弱点:它的灵活性和无类型的高级语法可能导致数据和计算密集型程序的性能不佳。
二、numba--高性能高效率的计算
在这篇文章中,我将向您介绍Numba,一个来自Anaconda的Python编译器,可以编译Python代码,以便在支持CUDA的GPU或多核CPU上执行。由于Python通常不是编译语言,您可能想知道为什么要使用Python编译器。答案当然是运行本机编译代码比运行动态解释代码快许多倍。 Numba允许您为Python函数指定类型签名,它可以在运行时进行编译(这是“即时”或JIT编译)。 Numba动态编译代码的能力意味着您不会放弃Python的灵活性。这是向高效率编程和高性能计算提供理想组合的重要一步。
使用Numba,现在可以编写标准的Python函数并在支持CUDA的GPU上运行它们。 Numba专为面向阵列的计算任务而设计,就像广泛使用的NumPy库一样。面向阵列的计算任务中的数据并行性非常适合GPU等加速器。 Numba了解NumPy数组类型,并使用它们生成有效的编译代码,以便在GPU或多核CPU上执行。所需的编程工作可以像添加函数装饰器一样简单,以指示Numba为GPU编译。例如,以下代码中的@vectorize装饰器在运行时生成标量函数Add的已编译矢量化版本,以便它可用于在GPU上并行处理数据数组
imort numpy as np from numba import vectorize @vectorize(['float32(float32, float32)'], target='cuda') def Add(a, b): return a + b # Initialize arrays N = 100000 A = np.ones(N, dtype=np.float32) B = np.ones(A.shape, dtype=A.dtype) C = np.empty_like(A, dtype=A.dtype) # Add arrays on GPU C = Add(A, B)
要在CPU上编译和运行相同的函数,我们只需将目标更改为“cpu”,从而在CPU上编译的矢量化C代码级别上产生性能。 这种灵活性可帮助您生成更多可重用的代码,并允许您在没有GPU的计算机上进行开发。
三、用于Python的GPU加速库
CUDA并行计算平台的优势之一是其广泛的GPU加速库。 Numba团队的另一个名为pyculib的项目为CUDA cuBLAS(密集线性代数),cuFFT(快速傅立叶变换)和cuRAND(随机数生成)库提供了Python接口。 许多应用程序只需使用这些库就可以获得显着的加速,而无需编写任何特定于GPU的代码。 例如,以下代码使用“XORWOW”伪随机数生成器在GPU上生成一百万个均匀分布的随机数。
import numpy as np from pyculib import rand as curand prng = curand.PRNG(rndtype=curand.PRNG.XORWOW) rand = np.empty(100000) prng.uniform(rand) print rand[:10]
四、与CUDA Python的大规模并行性
Anaconda(前身为Continuum Analytics)认识到,在某些计算上实现大幅加速需要一个更具表现力的编程接口,对并行性的控制要比库和自动循环矢量化所能提供的更详细。 因此,Numba具有另一组重要功能,可以构成非正式名称为“CUDA Python”的功能。 Numba公开CUDA编程模型,就像在CUDA C / C ++中一样,但是使用纯python语法,这样程序员就可以创建自定义的,调优的并行内核,而不会留下Python的舒适和优势。 Numba的CUDA JIT(可通过装饰器或函数调用获得)在运行时编译CUDA Python函数,专门针对您使用的类型,并且其CUDA Python API提供对数据传输和CUDA流的显式控制,以及其他功能
下面的代码示例使用简单的Mandelbrot设置内核演示了这一点。 请注意,mandel_kernel函数使用Numba提供的cuda.threadIdx,cuda.blockIdx,cuda.blockDim和cuda.gridDim结构来计算当前线程的全局X和Y像素索引。 与其他CUDA语言一样,我们通过在函数名称和参数列表之间插入一个“执行配置”(CUDA-代表用于运行内核的线程数和线程数)来启动内核:mandel_kernel [griddim,blockdim]( - 2.0,1.0,-1.0,1.0,d_image,20)。 您还可以看到使用to_host和to_device API函数将数据复制到GPU或从GPU复制数据。
@cuda.jit(device=True) def mandel(x, y, max_iters): """ Given the real and imaginary parts of a complex number, determine if it is a candidate for membership in the Mandelbrot set given a fixed number of iterations. """ c = complex(x, y) z = 0.0j for i in range(max_iters): z = z*z + c if (z.real*z.real + z.imag*z.imag) >= 4: return i return max_iters @cuda.jit def mandel_kernel(min_x, max_x, min_y, max_y, image, iters): height = image.shape[0] width = image.shape[1] pixel_size_x = (max_x - min_x) / width pixel_size_y = (max_y - min_y) / height startX = cuda.blockDim.x * cuda.blockIdx.x + cuda.threadIdx.x startY = cuda.blockDim.y * cuda.blockIdx.y + cuda.threadIdx.y gridX = cuda.gridDim.x * cuda.blockDim.x; gridY = cuda.gridDim.y * cuda.blockDim.y; for x in range(startX, width, gridX): real = min_x + x * pixel_size_x for y in range(startY, height, gridY): imag = min_y + y * pixel_size_y image[y, x] = mandel(real, imag, iters) gimage = np.zeros((1024, 1536), dtype = np.uint8) blockdim = (32, 8) griddim = (32,16) start = timer() d_image = cuda.to_device(gimage) mandel_kernel[griddim, blockdim](-2.0, 1.0, -1.0, 1.0, d_image, 20) d_image.to_host() dt = timer() - start print "Mandelbrot created on GPU in %f s" % dt imshow(gimage) On a server with an NVIDIA Tesla P100 GPU and an Intel Xeon E5-2698 v3 CPU, this CUDA Python Mandelbrot code runs nearly 1700 times faster than the pure Python version. 1700x may seem an unrealistic speedup, but keep in mind that we are comparing compiled, parallel, GPU-accelerated Python code to interpreted, single-threaded Py
在配备NVIDIA Tesla P100 GPU和Intel Xeon E5-2698 v3 CPU的服务器上,此CUDA Python Mandelbrot代码的运行速度比纯Python版快近1700倍。 1700x可能看起来不切实际,但请记住,我们正在将编译的,并行的,GPU加速的Python代码与CPU上的解释的单线程Python代码进行比较。、
四、 立即开始使用Numba
Numba为Python开发人员提供了一种轻松进入GPU加速计算的途径,以及使用日益复杂的CUDA代码并使用最少的新语法和术语的途径。您可以从简单的函数装饰器开始,自动编译您的函数,或使用pyculib公开的强大的CUDA库。随着您对并行编程概念的理解以及当您需要对并行线程进行表达和灵活控制时,CUDA可用,而无需您在第一天进入。
Numba是一个获得BSD许可的开源项目,它本身在很大程度上依赖于LLVM编译器的功能。 Numba的GPU后端使用基于LLVM的NVIDIA编译器SDK。 CUDA库周围的pyculib包装器也是开源和BSD许可的。
要开始使用Numba,第一步是下载并安装Anaconda Python发行版,这是一个“完全免费的企业级Python发行版,用于大规模数据处理,预测分析和科学计算”,其中包括许多流行的软件包(Numpy, Scipy,Matplotlib,iPython等)和“conda”,一个强大的包管理器。安装Anaconda后,键入conda install numba cudatoolkit pyculib安装所需的CUDA软件包。然后在ContinuumIO github存储库上查看CumbA的Numba教程。我还建议您查看Anaconda博客上的Numba帖子。