Готовый перевод PyCUDA documentation / Документация PyCUDA: Tutorial Introduction http://documen.tician.de/pycuda/tutorial.html
До того, как вы сможете использовать PyCUDA, вам нужно будет импортировать и инициализировать его:
import pycuda.driver as cuda
import pycuda.autoinit
from pycuda.compiler import SourceModule
Отметим, что вам не нужно использовать инициализацию pycuda.autoinit, контекстное создание, и очистку тоже можно произвести вручную, если вы этого желаете.
Передача данных
Следдующим шагом в большинстве программ является передача данных в прибор. В PyCUDA, вы будете в основном передавать данный через массивы numpy на хост.(но в действительности, все, что заставляет работать интерфейс буфера Pythona будет работать, даже строка). Давайте сделаем массив 4x4 случайных чисел:
import numpy
a = numpy.random.randn(4,4)
Но подождите - это совокупность чисел двойной точности, но большинство приборов nVidia поддерживают только числа одинарной точности:
a = a.astype(numpy.float32)
Наконец, нам нужно куда-то передавать данные, поэтому нам нужно выделить память на устройстве:
a_gpu = cuda.mem_alloc(a.nbytes)
В качестве последнего шага, нам нужно передать данные в GPU:
cuda.memcpy_htod(a_gpu, a)
Выполнение Ядра
В этом руководстве мы будем придерживаться чего-то простого: будем писать код в объекты из переменных двойной точности в a_gpu. К концу этой документации, мы напишем соответствующий им код Си на CUDA, и передадим этот код конструктору pycuda.compiler.SourceModule:
mod = SourceModule("""
__global__ void doublify(float *a)
{
int idx = threadIdx.x + threadIdx.y*4;
a[idx] *= 2;
}
""")
Если здесь нет никаких ошибок, то код теперь откомпилируется и загрузится в прибор. Мы нашли ссылку на нашу pycuda.driver.Function и вызвали ее, специфицируя a_gpu как аргумент и установив размер блока 4x4:
func = mod.get_function("doublify")
func(a_gpu, block=(4,4,1))
И наконец, мы произведем выборку данных из GPU и выведим их на экран вместе с оригинальным a:
a_doubled = numpy.empty_like(a)
cuda.memcpy_dtoh(a_doubled, a_gpu)
print a_doubled
print a
Этот кусок кода выведет, что-то похожее на это:
[[ 0.51360393 1.40589952 2.25009012 3.02563429]
[-0.75841576 -1.18757617 2.72269917 3.12156057]
[ 0.28826082 -2.92448163 1.21624792 2.86353827]
[ 1.57651746 0.63500965 2.21570683 -0.44537592]]
[[ 0.25680196 0.70294976 1.12504506 1.51281714]
[-0.37920788 -0.59378809 1.36134958 1.56078029]
[ 0.14413041 -1.46224082 0.60812396 1.43176913]
[ 0.78825873 0.31750482 1.10785341 -0.22268796]]
Она работает! На этом закончим наше пошаговое руководство. К счастью, PyCuda наследует(запоминает эти данные) это и все вам подчищает, поэтому вы закончили. Однако не пропустите некоторый бонусный материал в следующем разделе.
(Вы можете найти код для этого демо, по адресу examples/demo.py, в дистрибутиве исходников PyCUDA.)
Сочетания клавиш для явных копий памяти
Обработчик аргумента pycuda.driver.In, pycuda.driver.Out и pycuda.driver.InOut может упростить некоторые процессы передачи памяти. Например, вместо создания a_gpu, если замещение a прошло хорошо, можно использовать следующий код:
func(cuda.InOut(a), block=(4, 4, 1))
Подготовленные вызовы.
Вызов функции использует встроенный метод pycuda.driver.Function.__call__(), берущий на себя накладные расходы на определение типа(смотрите "Справочную документацию по интерфейсу устройств"). Чтобы достичь того же эффекта что и выше без этих накладных расходов, функций связывается с типами аргументов(как спроектировано в структурном модуле стандартной библиотеки Python), и затем вызывается. Это также помогает избежать объявления неявных размеров аргументов, используя классы numpy.number:
func.prepare("P", block=(4,4,1))
func.prepared_call((1, 1), a_gpu)
Бонус: Абстрагирование от осложнений.
Использование pycuda.gpuarray.GPUArray,тот же эффект можно достичь написанием значительно меньшего количества кода:
import pycuda.gpuarray as gpuarray
import pycuda.driver as cuda
import pycuda.autoinit
import numpy
a_gpu = gpuarray.to_gpu(numpy.random.randn(4,4).astype(numpy.float32))
a_doubled = (2*a_gpu).get()
print a_doubled
print a_gpu
Продвинутые темы
Структуры
(добавлено Nicholas Tung, найдите код в examples/demo_struct.py)
Предполагается, что у вас есть следующая структура, для удвоения числа переменных длин массивов:
mod = SourceModule("""
struct DoubleOperation {
int datalen, __padding; // поэтому 64-битные указатели могут быть выравнены
float *ptr;
};
__global__ void double_array(DoubleOperation *a) {
a = &a[blockIdx.x];
for (int idx = threadIdx.x; idx < a->datalen; idx += blockDim.x) {
a->ptr[idx] *= 2;
}
}
""")
Каждый блок в грид(смотрите документацию CUDA) удвоит один из массивов. Цикл for позволяет удвоить большее количиство элементов данных, чем потоки, хотя и не эффективен, если нельзя гарантировать, что будет необходимое количество потоков. Затем создается класс обертки для струкутры, и два массива инициализируются:
class DoubleOpStruct:
mem_size = 8 + numpy.intp(0).nbytes
def __init__(self, array, struct_arr_ptr):
self.data = cuda.to_device(array)
self.shape, self.dtype = array.shape, array.dtype
cuda.memcpy_htod(int(struct_arr_ptr), numpy.int32(array.size))
cuda.memcpy_htod(int(struct_arr_ptr) + 8, numpy.intp(int(self.data)))
def __str__(self):
return str(cuda.from_device(self.data, self.shape, self.dtype))
struct_arr = cuda.mem_alloc(2 * DoubleOpStruct.mem_size)
do2_ptr = int(struct_arr) + DoubleOpStruct.mem_size
array1 = DoubleOpStruct(numpy.array([1, 2, 3], dtype=numpy.float32), struct_arr)
array2 = DoubleOpStruct(numpy.array([0, 4], dtype=numpy.float32), do2_ptr)
print("original arrays", array1, array2)
Этот код использует функции pycuda.driver.to_device() и pycuda.driver.from_device(), чтобы выделить(локализировать) и скопировать значения и демонстрирует какие отступы выделенных блоков памяти могут использоваться. Наконец, код можно выполнить. Следующий код сначала демонстрирует удвоение обоих массивов, а затем только второй.
func = mod.get_function("double_array")
func(struct_arr, block = (32, 1, 1), grid=(2, 1))
print("doubled arrays", array1, array2)
func(numpy.intp(do2_ptr), block = (32, 1, 1), grid=(1, 1))
print("doubled second only", array1, array2, "\n")
Куда теперь направится
Когда вы почувствуете себя достаточно знакомыми с основами, можете свободно смотреть следующий раздел (Device Interface Reference Documentation). Для получения большего количества примеров, проверьте поддиректорию examples/ дистрибутива. Этот ярлык может также содержать некоторые тесты производительности, которые показывают разницу между вычислениями на GPU и CPU. В качестве руководства по тому, как это было сделано также может помочь PyCuda’s test suite, расположенный в поддиректории test/ дистрибутива.
Переведено на Нотабеноиде
http://notabenoid.org/book/20422/66858
Переводчики: wuffur
Настройки
Готово:
