CUDA 内核中的数组使用 Python 和 numba-pro
Arrays in CUDA Kernels using Python with numba-pro
我目前正在编写可以使用 GPU 进行高度并行化的代码。我的代码结构基本上是这样的:
- 创建两个数组,我们称它们为长度为 N 的 A 和 B。(CPU)
- 执行 NxN 次计算,最终 return 一个标量。这些计算仅依赖于 A 和 B,因此可以并行化。 (显卡)
- 将所有这些标量收集在一个列表中,并取最小的一个。 (CPU)
- 用这个标量修改 A 和 B (CPU)
- 返回步骤 2 并重复直到满足特定条件。
大多数示例都非常具有说明性,但它们似乎都是这样工作的:在 CPU 上执行代码的主要部分,并且仅在 GPU 上执行中间矩阵乘法等。特别是主机通常知道内核将要使用的所有变量。
对我来说恰恰相反,我想在 GPU 上执行大部分代码,而只在 CPU 本身上执行非常少量的步骤。我的主机对我的各个线程中发生的事情一无所知。它只管理标量列表以及我的数组 A 和 B。
因此我的问题是:
- 如何在内核中正确定义变量?特别是,我如何定义和 initialize arrays/lists?
- 如何编写 return 数组的设备函数? (s. 下面的 MatrixMultiVector 不起作用)
- 为什么我不能在 CUDA 内核中使用 numpy 和其他库?我有什么选择?
我目前拥有的示例如下所示:
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)
- 简短的回答是您不能在 CUDA 中定义动态列表或数组 Python。您可以静态定义本地或共享内存数组(请参阅文档中的
cuda.local.array()
和 cuda.shared.array
),但它们具有线程或块作用域,并且在其关联的线程或块退出后无法重用。但这就是所有支持的内容。您可以将外部定义的数组传递给内核,但它们的属性是只读的。
- 根据您的
myfuncArray
,您可以return一个外部定义的数组。您不能 return 动态定义的数组,因为内核不支持动态定义的数组(或与此相关的任何对象)。
- 您可以阅读 CUDA Python specification for yourself, but the really short answer is that CUDA Python is a superset of Numba's No Python Mode,虽然有可用的基本标量函数,但没有 Python 对象模型支持。这排除了很多 Python 功能,包括对象和 numpy。
我目前正在编写可以使用 GPU 进行高度并行化的代码。我的代码结构基本上是这样的:
- 创建两个数组,我们称它们为长度为 N 的 A 和 B。(CPU)
- 执行 NxN 次计算,最终 return 一个标量。这些计算仅依赖于 A 和 B,因此可以并行化。 (显卡)
- 将所有这些标量收集在一个列表中,并取最小的一个。 (CPU)
- 用这个标量修改 A 和 B (CPU)
- 返回步骤 2 并重复直到满足特定条件。
大多数示例都非常具有说明性,但它们似乎都是这样工作的:在 CPU 上执行代码的主要部分,并且仅在 GPU 上执行中间矩阵乘法等。特别是主机通常知道内核将要使用的所有变量。
对我来说恰恰相反,我想在 GPU 上执行大部分代码,而只在 CPU 本身上执行非常少量的步骤。我的主机对我的各个线程中发生的事情一无所知。它只管理标量列表以及我的数组 A 和 B。
因此我的问题是:
- 如何在内核中正确定义变量?特别是,我如何定义和 initialize arrays/lists?
- 如何编写 return 数组的设备函数? (s. 下面的 MatrixMultiVector 不起作用)
- 为什么我不能在 CUDA 内核中使用 numpy 和其他库?我有什么选择?
我目前拥有的示例如下所示:
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)
- 简短的回答是您不能在 CUDA 中定义动态列表或数组 Python。您可以静态定义本地或共享内存数组(请参阅文档中的
cuda.local.array()
和cuda.shared.array
),但它们具有线程或块作用域,并且在其关联的线程或块退出后无法重用。但这就是所有支持的内容。您可以将外部定义的数组传递给内核,但它们的属性是只读的。 - 根据您的
myfuncArray
,您可以return一个外部定义的数组。您不能 return 动态定义的数组,因为内核不支持动态定义的数组(或与此相关的任何对象)。 - 您可以阅读 CUDA Python specification for yourself, but the really short answer is that CUDA Python is a superset of Numba's No Python Mode,虽然有可用的基本标量函数,但没有 Python 对象模型支持。这排除了很多 Python 功能,包括对象和 numpy。