Массивы в ядрах CUDA, использующие Python с numba-pro
В настоящее время я пишу код, который можно сильно распараллелить с помощью графических процессоров. Моя структура кода по сути выглядит так:
- Создайте два массива, назовем их A и B длиной N. (CPU)
- Выполните вычисления NxN, которые в конечном итоге вернут скаляр. Эти вычисления зависят только от A и B и поэтому могут быть распараллелены. (ГПУ)
- Соберите все эти скаляры в списке и возьмите самый маленький. (ЦПУ)
- Изменить A и B с этим скаляром (CPU)
- Вернитесь к шагу 2 и повторяйте, пока не будет выполнено определенное условие.
Большинство примеров очень иллюстративны, но все они, кажется, работают следующим образом: выполнить основную часть кода на ЦП и выполнять только промежуточные умножения матриц и т. Д. На графическом процессоре. В частности, хост обычно знает все переменные, которые ядро собирается использовать.
Для меня это как раз наоборот, я хочу выполнить основную часть кода на GPU и только очень небольшое количество шагов на самом CPU. Мой хозяин буквально ничего не знает о том, что происходит внутри моих отдельных тем. Он управляет только списком скаляров, а также моими массивами A и B.
Поэтому мои вопросы:
- Как правильно определить переменные внутри ядра? В частности, как мне определить и инициализировать массивы / списки?
- Как мне написать функцию устройства, которая возвращает массив? (с. ниже MatrixMultiVector не работает)
- Почему я не могу использовать numpy и другие библиотеки внутри ядер CUDA? Какие у меня есть альтернативы?
Пример того, что у меня сейчас есть, выглядит так:
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 ответ
- Краткий ответ: вы не можете определять динамические списки или массивы в CUDA Python. Вы можете иметь статически определенные массивы локальной или общей памяти (см.
cuda.local.array()
а такжеcuda.shared.array
в документации), но они имеют область потока или блока и не могут быть повторно использованы после того, как их связанный поток или блок будет удален. Но это обо всем, что поддерживается. Вы можете передавать внешне определенные массивы ядрам, но их атрибуты доступны только для чтения. - Согласно вашему
myfuncArray
Вы можете вернуть внешне определенный массив. Вы не можете вернуть динамически определенный массив, потому что динамически определенные массивы (или любые объекты в этом отношении) не поддерживаются в ядрах. - Вы можете прочитать спецификацию CUDA Python для себя, но очень краткий ответ заключается в том, что CUDA Python является расширенным набором Numba's No Python Mode, и, хотя доступны элементарные скалярные функции, поддержка объектной модели Python отсутствует. Это исключает большую часть функциональности Python, включая объекты и numpy.