CUDA надає два різних API: Runtime API і Driver API. Вони обидва дуже схожі з точки зору базових задач, таких як робота з пам'яттю. Однак, між ними є деякі важливі відмінності. Найголовніша з них - це спосіб управління ядрами і то, як вони виконуються.
В оригінальному CUDA Runtime API ядра визначаються і компілюється разом з С-файлами. Вихідний код компілюється за допомогою NVCC - NVIDIA CUDA Compiler. Цей компілятор використовує інший компілятор C (наприклад, GCC або VS C), щоб відкомпілювати чистий C-код, після чого береться за компіляцію CUDA-складової - ядер і їх викликів (<<<.>>>). В результаті виходить здійсненний файл, який представляє цілу програму.
Звичайно ж, NVCC не може використовуватися для компіляції Java-програм. Синтаксис виклику ядер (<<<.>>>) неупотребім в Java, і після компіляції тут не створюється єдиного виконуваного файлу. Тому не існує можливості викликати свої власні ядра за допомогою JCuda Runtime API. Замість цього повинен використовуватися JCuda Driver API, як це буде показано нижче.
JCuda Runtime API в основному призначений для взаємодії з Java-прив'язками бібліотек CUDA Runtime, такими як JCublas і JCufft. Java-програміст, який хоче скористатися цими бібліотеками і не хоче створювати власні ядра може обійтися тільки JCuda Runtime API.
Перед тим, як використовувати JCuda, необхідно встановити драйвер і інструменти CUDA, які можна завантажити на сайті NVIDIA CUDA. Варто звернути увагу, що між випусками нових версій CUDA і JCuda можуть бути затримки. Для коректної інсталяції даного ПЗ, потрібно слідувати інструкціям на сайті.
SDK і приклади вихідних кодів для роботи JCuda не потрібні, але останні можуть бути корисні, щоб розібратися в загальних механізмах роботи CUDA.
Після коректної інсталяції CUDA слід завантажити потрібний архів JCuda в залежності від операційної системи. Архів містить JAR-файли і відповідні нативні бібліотеки.
JAR-файли потрібно додати в CLASSPATH, а нативні бібліотеки потрібно розташувати в доступному Java місці. У більшості випадків це або java.library.path конкретної JVM, або коренева директорія проекту. Також можна скористатися засобами операційної системи - наприклад, PATH або LD_LIBRARY_PATH.
первинне тестування
У цьому розділі дається пояснення, як виконати запуск проекту JCuda за допомогою консолі команд. Тим, хто знайомий з використанням JAR-файлів і нативних бібліотек в Java, можливо, варто пропустити цей розділ і перейти до створення нового проекту з коханою IDE. Інакше потрібно виконати наступні кроки:
- Скопіювати всі файли з завантаженого архіву JCuda в одну папку і додати туди файл "JCudaRuntimeTest.java" наступного змісту: import jcuda. *; import jcuda.runtime. *; public class JCudaRuntimeTest
> - Скомпілювати програму, виконавши наступну команду в директорії проекту (потрібно підставити актуальний номер замість "0.3.2a"): On Windows: javac -cp ".; Jcuda-0.3.2a.jar" JCudaRuntimeTest.java On Linux: javac -cp ". : jcuda-0.3.2a.jar "JCudaRuntimeTest.java Після цього буде створено файл" JCudaRuntimeTest.class "в цій же директорії.
- Запустити програму за допомогою наступної команди: On Windows: java -cp ".; Jcuda-0.3.2a.jar" JCudaRuntimeTest On Linux: java -cp ".: Jcuda-0.3.2a.jar" JCudaRuntimeTest Після цього буде виведена на екран інформація про створений в програмі покажчику.
створення ядер
Як було сказано у вступі, власні ядра можуть бути запущені з використанням Driver API. У цьому розділі описується стандартний робочий процес створення ядра в рамках JCuda. Велика частина інформації відноситься як до CUDA, так і JCuda. Так що будуть корисними ресурси, що описують CUDA - наприклад, CUDA Programming Guide.
написання ядра
Код ядра пишеться точно так же, як це робиться для CUDA. Зазвичай код ядра располагется в окремому файлі. У CUDA Runtime API ядро часто є частиною більшого файлу C. У такому випадку весь додатковий код буде проігнорований JCuda.
Потрібно приділити увагу наступного моменту: коли ядро буде викликано за допомогою Driver API, функція буде ідентифікуватися по імені. Тому її потрібно відзначити як extern "C", щоб компілятор C не змінив її ім'я. Приклад функції додавання векторів:
компіляція ядра
Вихідний код ядра повинен компілюватися за допомогою NVCC. В результаті буде створений файл, який може бути завантажений і виконаний за допомогою Driver API. Можливі два формати файлів для компіляції ядра:
- PTX - людино-якого читають (але досить складно-який розуміється) файл, який містить своєрідний "асемблерний" код.
- CUBIN (CUDA binary) - відкомпільований під конкретний GPU код, який буде виконуватися без додаткових витрат.
Раніше повсюдно використовувалися CUBIN-файли, але зараз перевага віддається PTX, які є переносяться і використовують компіляцію на льоту.
Завантаження і виконання ядра в JCuda
Процес завантаження і виконання ядра в JCuda Driver API з PTX- і CUBIN- файлів однаковий, так само, як і в CUDA Driver API. В першу чергу повинен бути завантажений модуль і отриманий покажчик на функцію ядра: // Load the ptx file. CUmodule module = new CUmodule (); cuModuleLoad (module, "JCudaVectorAddKernel.ptx"); // Obtain a function pointer to the kernel function. CUfunction function = new CUfunction (); cuModuleGetFunction (function, module, "add");
При виклику ядра виявляються деякі мовні обмеження Java. Функції установки параметрів ядра досить складні в версіях аж до CUDA 3.2, а також в CUDA 4.0. Ці функції були замінені однією, яка приймає всі параметри виконання ядра. Додатково вона приймає всі вхідні параметри ядра в якості одного void ** покажчика, який емулюється за допомогою класу Pointer. Таким чином, установка параметрів ядра в JCuda може бути навіть простіше, ніж в CUDA: // Set up the kernel parameters: A pointer to an array // of pointers which point to the actual values. Pointer kernelParameters = Pointer.to (Pointer.to (new int []), Pointer.to (deviceInputA), Pointer.to (deviceInputB), Pointer.to (deviceOutput)); // Call the kernel function. cuLaunchKernel (function, gridSizeX, 1, 1, // Grid dimension blockSizeX, 1, 1, // Block dimension 0, null, // Shared memory size and stream kernelParameters, null // Kernel- and extra parameters);
Варто відзначити, що тут проявляються проблеми, характерні для C - потрібно бути вкрай уважними при роботі з покажчиками.
В принципі, всі програми JCuda мають характерний патерн. Це дозволяє продовжити вивчення JCuda на основі прикладів.