Статьи

GPGPU Java Программирование

В одной из наших предыдущих публикаций мы обсуждали концепции и архитектуру Общей обработки на графическом процессоре (GPGPU). Для программистов на C / C ++ это все замечательно, но для программистов на Java написание C / C ++ вместо Java является, по меньшей мере, неудобством. Итак, какие инструменты существуют для программистов на Java?

Прежде чем мы углубимся в кодирование некоторого фона. Существует два конкурирующих пакета GPGPU: OpenCL и CUDA . OpenCL — это открытый стандарт, поддерживаемый всеми поставщиками графических процессоров (а именно AMD, NVIDIA и Intel), в то время как CUDA относится к NVIDIA и будет работать только на картах NVIDIA. Оба SDK поддерживают код C / C ++, что, конечно же, оставляет нас Java-разработчиками на холоде. До сих пор нет чистой поддержки OpenCL или CUDA в Java. Это не сильно поможет программисту на Java, которому нужно использовать огромный потенциал параллелизма графического процессора, если она не возится с интерфейсом Java Native. Конечно, есть некоторые инструменты Java, которые облегчают боль программирования Java GPGPU.

Два самых популярных (IMHO) — это jocl и jcuda . С этими инструментами вам все равно придется писать код на C / C ++, но, по крайней мере, это будет только для кода, который будет выполняться в GPU, что значительно минимизирует усилия.

На этот раз я посмотрю на jcuda и посмотрю, как мы можем написать простую программу GPGPU.

Давайте начнем с настройки среды разработки Linux CUDA GPGPU (хотя среды Windows и Mac также не должны быть сложными в настройке):

Шаг 1 : Установите графический процессор с поддержкой NVIDIA CUDA на свой компьютер. На сайте разработчиков NVIDIA есть список графических процессоров с поддержкой CUDA. Новые графические процессоры NVIDIA почти наверняка поддерживают CUDA, но на всякий случай проверьте спецификацию карты, чтобы убедиться…

Шаг 2 : Установите драйвер NVIDIA и CUDA SDK. Скачайте их и найдите инструкции по установке здесь .

Шаг 3 : Перейдите в каталог ~ / NVIDIA_GPU_Computing_SDK / C / src / deviceQuery и запустите make .

Шаг 4 : Если компиляция прошла успешно, перейдите в каталог ~ / NVIDIA_GPU_Computing_SDK / C / bin / linux / release и запустите файл deviceQuery . Вы получите много технической информации о вашей карте.

Вот что я получил для своей карты GeForce GT 430:

Обратите внимание на 2 мультипроцессора с 48 ядрами CUDA на 96 ядер, что неплохо для недорогой видеокарты стоимостью около 40 евро !!!!

Шаг 5 : Теперь, когда у вас есть среда CUDA, давайте напишем и скомпилируем программу CUDA на C. Напишите следующий код и сохраните его как multiply.cu

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">#include <iostream></span> #include <iostream></span>
 
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">__global__ void multiply(float a, float b, float* c)</span> __global__ void multiply (плавать a, плавать b, плавать * c)</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">{</span> {</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">*c=a*b;</span> * С = а * Ь;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
 
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">int main()</span> int main ()</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">{</span> {</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">float a, b, c;</span> плавать а, б, в;</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">float *c_pointer;</span> float * c_pointer;</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">a=1.35;</span> а = 1,35;</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">b=2.5;</span> б = 2,5;</span>
 
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cudaMalloc((void**)&c_pointer, sizeof(float));</span> cudaMalloc ((void **) & c_pointer, sizeof (float));</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">multiply<<<1,1>>>(a, b, c_pointer);</span> умножить <<< 1,1 >>> (a, b, c_pointer);</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cudaMemcpy(&c, c_pointer, sizeof(float),cudaMemcpyDeviceToHost);</span> cudaMemcpy (& c, c_pointer, sizeof (float), cudaMemcpyDeviceToHost);</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">/*** This is C!!!</span> / *** Это С !!!</span> <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">You manage your garbage on your own!</span> Вы сами управляете мусором!</span> <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">***/</span> *** /</span>   
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cudaFree(c_pointer);</span> cudaFree (c_pointer);</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">printf("Result = %f\n",c);</span> printf ("Результат =% f \ n", c);</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Скомпилируйте его с помощью компилятора cuda и запустите:

1
2
3
4
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">$ nvcc multiply.cu -o multiply</span> $ nvcc multiply.cu -o multiply</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">$ ./multiply</span> $ ./multiply</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Result = 3.375000</span> Результат = 3.375000</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">$</span> $</span>

Итак, что делает приведенный выше код? Функция умножения с квалификатором __global__ называется ядром и представляет собой фактический код, который будет выполняться в графическом процессоре. Код в главной функции выполняется в CPU как обычный код на C, хотя есть некоторые семантические различия:

  1. Функция умножения вызывается в скобках <<< 1,1 >>>. Два числа в скобках указывают CUDA, сколько раз код должен быть выполнен. CUDA позволяет нам создавать так называемые одно-, двух- или даже трехмерные потоковые блоки. Числа в этом примере указывают на один блок потока, работающий в одном измерении, таким образом, наш код будет выполнен 1 × 1 = 1 раз.
  2. Функции cudaMalloc, cudaMemcpy и cudaFree используются для обработки памяти графического процессора аналогично тому, как мы обрабатываем обычную память компьютера в C. Функция cudaMemcpy важна, поскольку у GPU есть собственная RAM, и прежде чем мы сможем обрабатывать любые данные в ядре нам нужно загрузить их в память GPU. Конечно, мы также должны скопировать результаты обратно в обычную память, когда закончите.

Теперь, когда мы получили основы того, как выполнять код в графическом процессоре, давайте посмотрим, как мы можем запускать код GPGPU из Java. Помните, что код ядра по-прежнему будет написан на C, но по крайней мере основной функцией теперь является java-код с помощью jcuda.

Загрузите двоичные файлы jcuda, разархивируйте их и убедитесь, что каталог, содержащий файлы .so (или .dll для Windows), указан либо в параметре java.library.path JVM, либо добавлен в переменную среды LD_LIBRARY_PATH (или ваша переменная PATH в windows). Точно так же файл jcuda-xxxxxxx.jar должен находиться в вашем classpath во время компиляции и выполнения вашей Java-программы.

Итак, теперь, когда у нас есть настройка jcuda, давайте посмотрим на наше jcuda-совместимое ядро:

1
2
3
4
5
6
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">extern "C"</span> внешний "C"</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">__global__ void multiply(float *a, float *b, float *c)</span> __global__ void multiply (float * a, float * b, float * c)</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">/*************** Kernel Code **************/</span> / *************** Код ядра ************** /</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">{</span> {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">c[0]= a[0] * b[0];</span> c [0] = a [0] * b [0];</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Вы заметите следующие отличия от предыдущего метода ядра:

  1. Мы используем квалификатор extern «C», чтобы сказать компилятору не смешивать имя метода умножения, чтобы мы могли вызывать его с его исходным именем.
  2. Мы используем массивы вместо примитивов для a, b и c. Это требуется jcuda, поскольку примитивы Java не поддерживаются jcuda. В jcuda данные передаются назад и вперед в GPU в виде массивов, таких как числа с плавающей запятой, целые числа и т. Д.

Сохраните этот файл как multiply2.cu . На этот раз мы не хотим компилировать файл как исполняемый файл, а скорее как библиотеку CUDA, которая будет вызываться в нашей java-программе. Мы можем скомпилировать наше ядро ​​в виде файла PTX или файла CUBIN. PTX — это удобочитаемые файлы, содержащие код, подобный сборке, который будет скомпилирован на лету. Файлы CUBIN являются скомпилированными CUda BINaries и могут вызываться напрямую, без компиляции на лету. Если вам не нужна оптимальная производительность при запуске, файлы PTX предпочтительнее, поскольку они не привязаны к конкретной вычислительной способности графического процессора, с которым они были скомпилированы, в то время как файлы CUBIN не будут работать на графических процессорах с меньшей вычислительной способностью.

Чтобы скомпилировать наше ядро, наберите следующее:

1
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">$ nvcc -ptx multiply2.cu -o multiply2.ptx</span> $ nvcc -ptx multiply2.cu -o multiply2.ptx</span>

Успешно создав наш файл PTX, давайте посмотрим на Java-эквивалент основного метода, который мы использовали в нашем примере C:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import static jcuda.driver.JCudaDriver.*;</span> импортировать статический jcuda.driver.JCudaDriver. *;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import jcuda.*;</span> импорт jcuda. ​​*;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import jcuda.driver.*;</span> импорт jcuda.driver. *;</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import jcuda.runtime.JCuda;</span> import jcuda.runtime.JCuda;</span>
 
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public class MultiplyJ {</span> открытый класс MultiplyJ {</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">public static void main(String[] args) {</span> public static void main (String [] args) {</span>
 
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">float[] a = new float[] {(float)1.35};</span> float [] a = new float [] {(float) 1.35};</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">float[] b = new float[] {(float)2.5};</span> float [] b = new float [] {(float) 2.5};</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">float[] c = new float[1];</span> float [] c = новый float [1];</span>
  
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuInit(0);</span> cuInit (0);</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">CUcontext pctx = new CUcontext();</span> CUcontext pctx = новый CUcontext ();</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">CUdevice dev = new CUdevice();</span> CUdevice dev = new CUdevice ();</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuDeviceGet(dev, 0);</span> cuDeviceGet (dev, 0);</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuCtxCreate(pctx, 0, dev);</span> cuCtxCreate (pctx, 0, dev);</span>
      
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">CUmodule module = new CUmodule();</span> Модуль CUmodule = новый CUmodule ();</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuModuleLoad(module, "multiply2.ptx");</span> cuModuleLoad (модуль, «multiply2.ptx»);</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">CUfunction function = new CUfunction();</span> Функция CUfunction = новая функция CUfunction ();</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuModuleGetFunction(function, module, "multiply");</span> cuModuleGetFunction (функция, модуль, «умножение»);</span>
 
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">CUdeviceptr a_dev = new CUdeviceptr();</span> CUdeviceptr a_dev = new CUdeviceptr ();</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuMemAlloc(a_dev, Sizeof.FLOAT);</span> cuMemAlloc (a_dev, Sizeof.FLOAT);</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuMemcpyHtoD(a_dev, Pointer.to(a), Sizeof.FLOAT);</span> cuMemcpyHtoD (a_dev, Pointer.to (a), Sizeof.FLOAT);</span>
 
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">CUdeviceptr b_dev = new CUdeviceptr();</span> CUdeviceptr b_dev = new CUdeviceptr ();</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuMemAlloc(b_dev, Sizeof.FLOAT);</span> cuMemAlloc (b_dev, Sizeof.FLOAT);</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuMemcpyHtoD(b_dev, Pointer.to(b), Sizeof.FLOAT);</span> cuMemcpyHtoD (b_dev, Pointer.to (b), Sizeof.FLOAT);</span>
 
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">CUdeviceptr c_dev = new CUdeviceptr();</span> CUdeviceptr c_dev = new CUdeviceptr ();</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuMemAlloc(c_dev, Sizeof.FLOAT);</span> cuMemAlloc (c_dev, Sizeof.FLOAT);</span>
 
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Pointer kernelParameters = Pointer.to(</span> Pointer kernelParameters = Pointer.to (</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Pointer.to(a_dev),</span> Pointer.to (a_dev),</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Pointer.to(b_dev),</span> Pointer.to (b_dev),</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Pointer.to(c_dev)</span> Pointer.to (c_dev)</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">);</span> );</span>
 
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuLaunchKernel(function, 1, 1, 1, 1, 1, 1, 0, null, kernelParameters, null);</span> cuLaunchKernel (function, 1, 1, 1, 1, 1, 1, 0, null, kernelParameters, null);</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">cuMemcpyDtoH(Pointer.to(c), c_dev, Sizeof.FLOAT);</span> cuMemcpyDtoH (Pointer.to (c), c_dev, Sizeof.FLOAT);</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">JCuda.cudaFree(a_dev);</span> JCuda.cudaFree (a_dev);</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">JCuda.cudaFree(b_dev);</span> JCuda.cudaFree (b_dev);</span>
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">JCuda.cudaFree(c_dev);</span> JCuda.cudaFree (c_dev);</span>
 
     <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">System.out.println("Result = "+c[0]);</span> System.out.println ("Result =" + c [0]);</span>
  <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Хорошо, это похоже на большой код для простого умножения двух чисел, но помните, что существуют ограничения относительно указателей Java и C. Итак, начиная со строк с 9 по 11, мы преобразуем наши параметры a, b и c в массивы с именами a, b и c, каждый из которых содержит только одно число с плавающей точкой.

В строках с 13 по 17 мы сообщаем jcuda, что будем использовать первый графический процессор в нашей системе (в высокопроизводительных системах можно использовать более одного графического процессора).

В строках с 19 по 22 мы сообщаем jcuda, где находится наш PTX-файл, и имя метода ядра, который мы хотели бы использовать (в нашем случае умножение .)

Вещи становятся интересными в строке 24, где мы используем специальный класс jcuda CUdeviceptr, который действует как заполнитель указателя. В строке 25 мы используем указатель CUdeviceptr, который мы только что создали для выделения памяти GPU. Обратите внимание, что если бы в нашем массиве было более одного элемента, нам нужно было бы умножить константу Sizeof.FLOAT на количество элементов в нашем массиве. Наконец, в строке 26 мы копируем содержимое нашего первого массива в графический процессор. Точно так же мы создаем наш указатель и копируем содержимое в ОЗУ графического процессора для нашего второго массива (b). Для нашего выходного массива (c) нам нужно только выделить память GPU.

В строке 35 мы создаем объект Pointer, который будет содержать все параметры, которые мы хотим передать нашему методу умножения .

Мы выполняем наш код ядра в строке 41, где мы выполняем служебный метод cuKernelLaunch, передавая классы функций и указателей в качестве параметров. Первые шесть параметров после параметра функции определяют количество сеток (сетка — это группа блоков) и блоков, которые в нашем примере равны 1, так как мы будем выполнять ядро ​​только один раз. Следующие два параметра — 0 и ноль и используются для идентификации любой разделяемой памяти (памяти, которая может быть разделена между потоками), которую мы, возможно, определили, в нашем случае ни одного. Следующий параметр содержит созданный нами объект Pointer, содержащий указатели на устройства a, b, c, а последний параметр предназначен для дополнительных опций.

После возвращения нашего ядра мы просто копируем содержимое dev_c в наш массив c, освобождаем всю память, выделенную в GPU, и печатаем результат, сохраненный в c [0], что, конечно, так же, как в нашем примере на C.

Вот как мы компилируем и выполняем программу MultiplyJ.java (при условии, что multiply2.ptx находится в том же каталоге):

1
2
3
4
5
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">$ javac -cp ~/GPGPU/jcuda/JCuda-All-0.4.0-beta1-bin-linux-x86_64/jcuda-0.4.0-beta1.jar MultiplyJ.java</span> $ javac -cp ~ / GPGPU / jcuda / JCuda-All-0.4.0-beta1-bin-linux-x86_64 / jcuda-0.4.0-beta1.jar MultiplyJ.java</span>
 
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">$ java -cp ~/GPGPU/jcuda/JCuda-All-0.4.0-beta1-bin-linux-x86_64/jcuda-0.4.0-beta1.jar:.</span> $ java -cp ~ / GPGPU / jcuda / JCuda-All-0.4.0-beta1-bin-linux-x86_64 / jcuda-0.4.0-beta1.jar :.</span> <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">MultiplyJ</span> MultiplyJ</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">Result = 3.375</span> Результат = 3,375</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">$</span> $</span>

Обратите внимание, что в этом примере каталог ~ / GPGPU / jcuda / JCuda-All-0.4.0-beta1-bin-linux-x86_64 уже находится в моем LD_LIBRARY_PATH, поэтому мне не нужно устанавливать параметр java.library.path в JVM.

Надеюсь, к настоящему моменту механика jcuda ясна, хотя мы на самом деле не затрагивали истинную силу GPU — массовый параллелизм. В следующей статье я приведу пример того, как запускать параллельные потоки в CUDA с использованием Java, а также пример того, что НЕ нужно запускать в графическом процессоре. Обработка на GPU имеет смысл для очень специализированных задач, и большинство задач лучше оставить для обработки нашим старым и доверенным процессором.

Справка: GPGPU Java Programming от нашего партнера W4G Спироса Сакеллариу .

Статьи по Теме :