使用Python与numba-pro进行CUDA内核中的数组

时间:2015-10-30 13:29:05

标签: python cuda numba-pro

我目前正在编写可以使用GPU进行大量并行化的代码。我的代码结构基本上如下所示:

  1. 创建两个数组,让我们称它们为长度为N的A和B.(CPU)
  2. 执行最终返回标量的NxN计算。这些计算仅取决于A和B,因此可以并行化。 (GPU)
  3. 将所有这些标量集中在一个列表中,然后选择最小的标量。 (CPU)
  4. 使用此标量(CPU)修改A和B
  5. 返回步骤2并重复直至达到某个条件。
  6. 大多数示例都非常具有说明性,但它们似乎都是这样的:在CPU上执行代码的主要部分,并且仅在GPU上执行中间矩阵乘法等。特别是主机通常知道内核将要使用的所有变量。

    对我来说,反之亦然,我想在GPU上执行代码的主要部分,并且只需要在CPU本身上执行非常少量的步骤。我的主人几乎不知道我个人线程中发生的事情。它只管理标量列表以及我的数组A和B.

    我的问题是:

    1. 如何在内核中正确定义变量?特别是,如何定义和初始化数组/列表?
    2. 如何编写返回数组的设备函数? (在MatrixMultiVector下面不起作用)
    3. 为什么我不能在CUDA内核中使用numpy和其他库?我有什么替代品?
    4. 我目前拥有的一个例子如下:

      from __future__ import division
      import numpy as np
      from numbapro import *
      
      
      # Device Functions
      #----------------------------------------------------------------------------------------------------------------------------------------------------------------------
      
      # Works and can be called corrently from TestKernel Scalar
      @cuda.jit('float32(float32, float32)', device=True)
      def myfuncScalar(a, b):
          return a+b;
      
      
      # Works and can be called correctly from TestKernel Array
      @cuda.jit('float32[:](float32[:])', device=True)
      def myfuncArray(A):
          for k in xrange(4):
              A[k] += 2*k;
          return A
      
      
      # Takes Matrix A and Vector v, multiplies them and returns a vector of shape v. Does not even compile.
      # Failed at nopython (nopython frontend), Only accept returning of array passed into the function as argument
      # But v is passed to the function as argument...
      
      @cuda.jit('float32[:](float32[:,:], float32[:])', device=True)
      def MatrixMultiVector(A,v):
          tmp = cuda.local.array(shape=4, dtype=float32); # is that thing even empty? It could technically be anything, right?
          for i in xrange(A[0].size):
              for j in xrange(A[1].size):
                  tmp[i] += A[i][j]*v[j];
          v = tmp;
          return v;
      
      
      
      # Kernels
      #----------------------------------------------------------------------------------------------------------------------------------------------------------------------
      
      # TestKernel Scalar - Works
      @cuda.jit(void(float32[:,:]))
      def TestKernelScalar(InputArray):
          i = cuda.grid(1)
          for j in xrange(InputArray[1].size):
              InputArray[i,j] = myfuncScalar(5,7);
      
      
      # TestKernel Array
      @cuda.jit(void(float32[:,:]))
      def TestKernelArray(InputArray):
      
          # Defining arrays this way seems super tedious, there has to be a better way.
          M = cuda.local.array(shape=4, dtype=float32);
          M[0] = 1; M[1] = 0; M[2] = 0; M[3] = 0;
      
          tmp = myfuncArray(M);
          #tmp = MatrixMultiVector(A,M); -> we still have to define a 4x4 matrix for that.
      
          i = cuda.grid(1)
          for j in xrange(InputArray[1].size):
              InputArray[i,j] += tmp[j];
      
      #----------------------------------------------------------------------------------------------------------------------------------------------------------------------
      # Main
      #----------------------------------------------------------------------------------------------------------------------------------------------------------------------
      
      N = 4;
      
      C = np.zeros((N,N), dtype=np.float32);
      TestKernelArray[1,N](C);
      
      print(C)
      

1 个答案:

答案 0 :(得分:1)

  1. 简短的回答是,您无法在CUDA Python中定义动态列表或数组。您可以拥有静态定义的本地或共享内存数组(请参阅文档中的cuda.local.array()cuda.shared.array),但这些数组具有线程或块范围,并且在其关联的线程或块之后无法重用退役。但这是所有支持的。您可以将外部定义的数组传递给内核,但它们的属性是只读的。
  2. 根据您的myfuncArray,您可以返回外部定义的数组。您无法返回动态定义的数组,因为内核不支持动态定义的数组(或任何对象)。
  3. 你可以自己阅读CUDA Python specification,但真正简短的回答是CUDA Python是Numba的No Python Mode的超集,虽然有基本的标量函数,但有没有Python对象模型支持。这排除了很多Python功能,包括对象和numpy。