Використання Java з графічними процесорами Nvidia (CUDA)


144

Я працюю над бізнес-проектом, який робиться на Java, і для його обчислення потрібна величезна обчислювальна сила. Проста математика, але з величезною кількістю даних.

Ми наказали спробувати деякі графічні процесори CUDA, і оскільки Java не підтримується CUDA, мені цікаво, з чого почати. Чи варто створити інтерфейс JNI? Чи варто використовувати JCUDA чи є інші способи?

Я не маю досвіду в цій галузі, і я хотів би, якби хтось міг спрямувати мене на щось, щоб я міг почати досліджувати та вчитися.


2
GPU допоможуть вам прискорити конкретні типи обчислювальної проблеми. Однак якщо у вас величезна кількість даних, ви, швидше за все, пов'язані введенням в дію. Швидше за все, GPU не є рішенням.
кухар Стів

1
"Підвищення продуктивності Java за допомогою GPGPU" -> arxiv.org/abs/1508.06791
BlackBear

4
Відкрите питання, я радий, що модники не закрили його, тому що відповідь від Marco13 неймовірно корисна! Має бути вікі IMHO
JimLohse

Відповіді:


443

Перш за все, ви повинні знати про те, що CUDA не буде автоматично робити обчислення швидше. З одного боку, тому що програмування GPU - це мистецтво, і це може бути дуже складним, щоб виправити це правильно . З іншого боку, оскільки графічні процесори добре підходять лише для певних видів обчислень.

Це може здатися заплутаним, оскільки ви можете в основному обчислити що- небудь на GPU. Ключовим моментом є, звичайно, чи вдасться ви досягти хорошої швидкості чи ні. Найважливіша класифікація тут полягає в тому, чи є проблема паралельною задачі чи паралельною даною . Перший стосується, грубо кажучи, проблем, коли декілька ниток працюють над власними завданнями, більш-менш незалежно. Другий стосується проблем, коли багато потоків роблять те саме - але в різних частинах даних.

Останнє - це проблема, з якою добре справляються GPU: У них багато ядер, і всі ядра роблять те саме, але працюють на різних частинах вхідних даних.

Ви згадали, що у вас "проста математика, але з величезною кількістю даних". Хоча це може виглядати як ідеально паралельна проблема даних і, таким чином, як це було добре підходить для GPU, є ще один аспект, який слід врахувати: GPU є смішно швидкими з точки зору теоретичної обчислювальної потужності (FLOPS, Operating Floating Point Operations per Second). Але їх часто пригнічує пропускна здатність пам'яті.

Це призводить до іншої класифікації проблем. А саме, чи пов'язані проблеми з пам'яттю або з обчисленням .

Перший стосується проблем, коли кількість інструкцій, виконаних для кожного елемента даних, є низькою. Наприклад, розглянемо паралельне векторне додавання: Вам доведеться прочитати два елементи даних, потім виконати одне додавання і потім записати суму в вектор результату. Ви не побачите прискорення, роблячи це в GPU, тому що одне доповнення не компенсує зусиль з читання / запису пам'яті.

Другий термін, "обчислений зв'язаний", відноситься до проблем, коли кількість вказівок є великою порівняно з кількістю читання / запису пам'яті. Наприклад, розглянемо множення матриці: Кількість інструкцій буде O (n ^ 3), коли n - розмір матриці. У цьому випадку можна очікувати, що графічний процесор перевершить процесор за певним розміром матриці. Іншим прикладом може бути, коли багато складних тригонометричних обчислень (синус / косинус тощо) виконуються на елементах даних "мало".

Як правило: Ви можете припустити, що читання / запис одного елемента даних з "основного" пам'яті GPU має затримку близько 500 інструкцій ....

Отже, ще одним ключовим моментом для роботи графічних процесорів є локалізація даних : Якщо вам доведеться читати чи записувати дані (і в більшості випадків вам доведеться ;-)), то ви повинні переконатися, що дані зберігаються настільки ж близько, як можливо до ядер GPU. Таким чином, графічні процесори мають певні області пам'яті (їх називають "локальною пам'яттю" або "спільною пам'яттю"), які зазвичай мають розмір лише декілька КБ, але особливо ефективні для даних, які збираються залучатись до обчислення.

Отже, щоб ще раз наголосити на цьому: програмування GPU - це мистецтво, яке лише віддалено пов'язане з паралельним програмуванням на процесорі. Такі речі, як Threads in Java, з усією інфраструктурою паралельності тощо ThreadPoolExecutors, ForkJoinPoolsможуть створити враження, що вам просто потрібно якось розділити свою роботу і розподілити її між декількома процесорами. На графічному процесорі ви можете зіткнутися з проблемами на набагато нижчому рівні: Зайнятість, реєстраційний тиск, загальний тиск пам'яті, об'єм пам'яті ... лише декілька.

Однак, коли у вас є проблема, паралельна даних, пов'язана з обчисленнями для вирішення, GPU - це шлях.


Загальне зауваження: Ви спеціально просили CUDA. Але я настійно рекомендую вам також ознайомитися з OpenCL. Він має ряд переваг. Перш за все, це незалежний від постачальників відкритий галузевий стандарт, а також є реалізація OpenCL від AMD, Apple, Intel та NVIDIA. Крім того, існує набагато ширша підтримка OpenCL у світі Java. Єдиний випадок, коли я вважаю за краще використовувати CUDA - це коли ви хочете використовувати бібліотеки виконання CUDA, наприклад, CUFFT для FFT або CUBLAS для BLAS (матричні / векторні операції). Хоча існують підходи щодо надання подібних бібліотек для OpenCL, вони не можуть безпосередньо використовуватися з боку Java, якщо ви не створите власні прив'язки JNI для цих бібліотек.


Вам також може бути цікаво почути, що в жовтні 2012 року група OpenJDK HotSpot розпочала проект "Суматра": http://openjdk.java.net/projects/sumatra/ . Мета цього проекту - надати підтримку GPU безпосередньо в JVM, за підтримки JIT. Поточний стан та перші результати можна побачити у списку розсилки за адресою http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev


Однак деякий час тому я зібрав деякі ресурси, пов’язані з "Java в GPU" взагалі. Я знову їх підсумую тут, не в конкретному порядку.

( Відмова : Я автор http://jcuda.org/ та http://jocl.org/ )

Переклад коду (байт) та генерація коду OpenCL:

https://github.com/aparapi/aparapi : Бібліотека з відкритим кодом, яку створює та активно підтримує AMD. У спеціальному класі "Ядро" можна перекрити конкретний метод, який слід виконувати паралельно. Байт-код цього методу завантажується під час виконання за допомогою власного зчитувача байтових кодів. Код переводиться в код OpenCL, який потім компілюється за допомогою компілятора OpenCL. Потім результат може бути виконаний на пристрої OpenCL, який може бути графічним процесором або процесором. Якщо компіляція в OpenCL неможлива (або немає OpenCL), код все одно буде виконуватися паралельно, використовуючи пул потоків.

https://github.com/pcpratts/rootbeer1 : Бібліотека з відкритим кодом для перетворення частин Java в програми CUDA. Він пропонує спеціалізовані інтерфейси, які можуть бути реалізовані для вказівки на те, що певний клас повинен виконуватися на GPU. На відміну від Aparapi, він намагається автоматично серіалізувати "відповідні" дані (тобто повну відповідну частину об'єктного графіка!) У подання, яке підходить для GPU.

https://code.google.com/archive/p/java-gpu/ : Бібліотека для перекладеного кодованого коду Java (з деякими обмеженнями) в код CUDA, який потім компілюється в бібліотеку, яка виконує код у GPU. Бібліотека була розроблена в контексті кандидатської дисертації, яка містить глибоку довідкову інформацію про процес перекладу.

https://github.com/ochafik/ScalaCL : Прив'язки Scala для OpenCL. Дозволяє обробляти спеціальні колекції Scala паралельно з OpenCL. Функції, які викликаються елементами колекцій, можуть бути звичайними функціями Scala (з деякими обмеженнями), які потім переводяться у ядра OpenCL.

Розширення мови

http://www.ateji.com/px/index.html : Мовне розширення для Java, яке дозволяє паралельні конструкції (наприклад, паралель для циклів, стиль OpenMP), які потім виконуються на GPU з OpenCL. На жаль, цей дуже перспективний проект більше не підтримується.

http://www.habanero.rice.edu/Publications.html (JCUDA): Бібліотека, яка може перевести спеціальний код Java (званий JCUDA-код) у код Java та CUDA-C, який потім може бути скомпільований і виконаний на GPU. Однак, схоже, бібліотека не є загальнодоступною.

https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html : розширення мови Java для конструкцій OpenMP, із заднім числом CUDA

Java OpenCL / CUDA обов'язкові бібліотеки

https://github.com/ochafik/JavaCL : Прив'язки Java для OpenCL: Об'єктно-орієнтована бібліотека OpenCL, заснована на автоматично сформованих прив'язках низького рівня

http://jogamp.org/jocl/www/ : Прив'язки Java для OpenCL: об'єктно-орієнтована бібліотека OpenCL, заснована на автоматично створених прив'язках низького рівня

http://www.lwjgl.org/ : Прив’язки Java для OpenCL: Автоматично створені прив'язки низького рівня та об'єктно-орієнтовані класи зручності

http://jocl.org/ : Прив'язки Java для OpenCL: Прив'язки низького рівня, які є відображенням оригіналу OpenCL API 1: 1.

http://jcuda.org/ : Прив'язки Java для CUDA: Прив'язки низького рівня, які є відображенням оригінального API CUDA 1: 1.

Різне

http://sourceforge.net/projects/jopencl/ : Прив'язки Java для OpenCL. Здається, це більше не підтримується з 2010 року

http://www.hoopoe-cloud.com/ : Прив'язки Java для CUDA. Здається, це більше не підтримується



розглянемо операцію додавання 2 матриць і збереження результату в третій матриці. Коли mutli є потоковим процесором без OpenCL, вузьке місце завжди буде кроком, на якому відбувається додавання. Ця операція, очевидно, паралельна даних. Але давайте скажемо, що ми не знаємо, чи буде вона заздалегідь обчислена чи пам'ять. Це потребує багато часу та ресурсів, щоб реалізувати, а потім побачити, що процесор набагато краще виконує цю операцію. Тоді як заздалегідь ідентифікувати це, не застосовуючи код OpenCL.
Cool_Coder

2
@Cool_Coder Дійсно, заздалегідь важко сказати, чи (або наскільки) певна задача матиме користь від реалізації GPU. Для першого відчуття кишок, напевно, потрібен певний досвід роботи з різними випадками використання (чого, правда, теж насправді немає). Першим кроком може стати перегляд nvidia.com/object/cuda_showcase_html.html і побачити, чи є в списку "подібні" проблеми. (Це CUDA, але вона концептуально наближена до OpenCL, що результати можна передавати в більшості випадків). У більшості випадків також згадується прискорення, і багато з них мають посилання на документи або навіть код
Marco13,

+1 для aparapi - це простий спосіб розпочати роботу з opencl в java, і дозволяє легко порівнювати продуктивність процесора з графічним процесором для простих випадків. Крім того, він підтримується AMD, але добре працює з картами Nvidia.
кухар Стів

12
Це один з найкращих відповідей, які я коли-небудь бачив на StackOverflow. Дякую за витрачений час та зусилля!
ViggyNash

1
@AlexPunnen Це, мабуть, поза рамками коментарів. Наскільки мені відомо, OpenCV має деяку підтримку CUDA, як на docs.opencv.org/2.4/modules/gpu/doc/introduction.html . У developer.nvidia.com/npp є багато процедур обробки зображень, які можуть бути зручними. І github.com/GPUOpen-ProfessionalCompute-Tools/HIP може бути "альтернативою" для CUDA. Це може бути можливим , щоб запитати як нове питання, але один повинен бути обережним , щоб висловити це правильно, щоб уникнути downvotes на «основі думки» / «просять сторонніх бібліотек» ...
Marco13


2

З проведених нами досліджень , якщо ви орієнтуєтесь на графічні процесори Nvidia і вирішили використовувати CUDA через OpenCL , я знайшов три способи використання API CUDA в Java.

  1. JCuda (або альтернатива) - http://www.jcuda.org/ . Це здається найкращим рішенням проблем, над якими я працюю. Багато бібліотек, таких як CUBLAS, доступні в JCuda. Ядра все ще пишуться на С, хоча.
  2. Інтерфейси JNI - JNI не є моїм улюбленим для написання, але вони дуже потужні і дозволять вам робити все, що CUDA може зробити.
  3. JavaCPP - Це, в основному, дозволяє створювати JNI інтерфейс на Java, не записуючи код C безпосередньо. Тут є приклад: який найпростіший спосіб запустити робочий код CUDA на Java? про те, як використовувати це з тягою CUDA. Мені здається, ви можете також просто написати інтерфейс JNI.

Усі ці відповіді - це лише способи використання коду C / C ++ на Java. Ви повинні запитати себе, чому вам потрібно використовувати Java, а якщо ви не можете це зробити на C / C ++.

Якщо вам подобається Java і ви знаєте, як її використовувати, і не хочете працювати з усім управлінням вказівниками, а що - ні, що поставляється з C / C ++, то JCuda - це, мабуть, відповідь. З іншого боку, бібліотека CUDA Thrust та інші бібліотеки, подібні до неї, можуть використовуватися для багато управління вказівниками в C / C ++, і, можливо, ви повинні це подивитися.

Якщо вам подобається C / C ++ і ви не заперечуєте проти управління вказівниками, але є й інші обмеження, які змушують вас використовувати Java, то JNI може бути найкращим підходом. Хоча, якщо ваші методи JNI просто стануть обгортками для команд ядра, ви також можете просто використовувати JCuda.

Є кілька альтернатив JCuda, таких як Cuda4J та Root Beer, але, схоже, вони не підтримуються. В той час як на момент написання цього JCuda підтримує CUDA 10.1. який є найсучаснішим SDK CUDA.

Крім того, є кілька бібліотек Java, які використовують CUDA, такі як deepplearning4j та Hadoop, які можуть робити те, що ви шукаєте, не вимагаючи від вас безпосередньо писати код ядра. Я не надто заглядав у них.


1

Marco13 вже дав чудову відповідь .

Якщо ви шукаєте спосіб використання графічного процесора без реалізації ядер CUDA / OpenCL, я хотів би додати посилання на розширення finmath-lib-cuda (finmath-lib-gpu-extensions) http: // finmath .net / finmath-lib-cuda-extensions / (відмова від відповідальності: я є підтримувачем цього проекту).

Проект передбачає реалізацію "векторних класів", якщо бути точним, інтерфейс RandomVariable, який називається , що забезпечує арифметичні операції та скорочення на вектори. Є реалізація для процесора та GPU. Є реалізація за допомогою алгоритмічної диференціації або звичайних оцінок.

Поліпшення продуктивності GPU наразі невеликі (але для векторів розміром 100 000 ви можете отримати коефіцієнт> 10 покращення продуктивності). Це пов’язано з невеликими розмірами ядра. Це покращиться в майбутній версії.

Реалізація GPU використовує JCuda та JOCL та доступна для графічних процесорів Nvidia та ATI.

Бібліотека Apache 2.0 та доступна через Maven Central.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.