用Python Numba库写CUDA程序(一)

Numba库

Numba,将Python代码编译生成优化后的机器码,提高代码效率。

Numba, a Python compiler from Anaconda that can compile Python code
for execution on CUDA-capable GPUs, provides Python developers with an
easy entry into GPU-accelerated computing and a path for using
increasingly sophisticated CUDA code with a minimum of new syntax and
jargon.

语法

1.函数定义前加@jit,默认是nopython模式,如果因为某些原因无法使用,则会使用object模式。

@jit(nopython=True) #能获得最佳性能。函数的运行与Python解释器完全无关,不会调用Python的C语言API。

@njit:等价于@jit(nopython=True)

@njit #要求函数中所有变量的类型都可以被编译器推导(一些基本类型,如不能是一些库或自己定义的数据类型等),否则就会报错
object #自动识别函数中循环语句等可以编译加速的代码部分,并编译成机器码,但是不能获取最佳性能

2.jit也提供了参数,叫做function signature。jit后指定函数的输入输出数据类型,可以获得轻微的速度提升,因为编译器不需要在编译的时候自动推导类型了。
这里jit后面的就是function signature。float64表示输出数据类型,int32表示输入数据类型。如果嫌太麻烦还可以简写成@jit(f8(i4,i4))

@jit(float64(int32, int32))
def f(x, y):
    # A somewhat trivial example
    return (x + y) / 3.14

3.编译会消耗一定的时间。编译好之后Numba会将机器码先缓存起来,第二次再调用的时候就不会再编译而是直接运行了。统计第二次运行的时间!
4.矩阵
添加一个函数装饰器以指示Numba为GPU进行编译。例如,@vectorize后的代码,Add 运行时生成标量函数的编译后的矢量化版本,以便可以将其用于在GPU上并行处理数据数组。要在CPU上编译并运行相同的功能,我们只需将target更改为“ cpu”。

import 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)

5.其他GPU加速库
pyculib, provides a Python interface to the CUDA cuBLAS (dense linear algebra), cuFFT (Fast Fourier Transform), and cuRAND (random number generation) libraries.仅通过使用这些库,许多应用程序就可以大大提高速度,而无需编写任何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]

6.实例Mandelbrot set。全代码见此 mandelbrot_numba.ipynb
Mandelbrot set原理
f c ( z ) = z 2 + c f c ( z ) = z 2 + c {\displaystyle f_{c}(z)=z^{2}+c}{\displaystyle f_{c}(z)=z^{2}+c} fc(z)=z2+cfc(z)=z2+c从z=0开始迭代, z n + 1 = z n 2 + c {\displaystyle z_{n+1}=z_{n}^{2}+c} zn+1=zn2+c

@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)
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值