Использование Java с графическим процессором Nvidia (cuda)

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

Мы заказали cuda gpu попробовать, и поскольку Java не поддерживается cuda, мне интересно, с чего начать. Должен ли я строить интерфейс JNI? Должен ли я использовать JCUDA или есть другие способы пойти с этим?

У меня нет опыта в этой области, и я бы хотел, чтобы кто-то мог направить меня к чему-то, чтобы я мог начать исследования и обучение.

Ответ 1

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

Это может показаться запутанным, потому что вы можете в принципе вычислить что-либо на графическом процессоре. Ключевым моментом является, конечно, то, достигнете ли вы хорошего ускорения или нет. Наиболее важная классификация здесь заключается в том, является ли проблема задачей параллельной или параллельной данными. Грубо говоря, первый относится к проблемам, когда несколько потоков работают над своими задачами более или менее независимо. Второй относится к проблемам, когда многие потоки все делают то же самое, но на разных частях данных.

Это проблема, с которой работают графические процессоры: у них много ядер, и все ядра делают то же самое, но работают на разных частях входных данных.

Вы упомянули, что у вас есть "простая математика, но с огромным количеством данных". Хотя это может показаться совершенно параллельной с данными проблемой, и, как это было хорошо подходит для графического процессора, есть еще один аспект: графические процессоры являются смехотворно быстрыми с точки зрения теоретической вычислительной мощности (FLOPS, Floating Point Operations Per Second). Но они часто сдерживаются пропускной способностью памяти.

Это приводит к другой классификации проблем. А именно, являются ли проблемы привязаны к памяти или вычислять привязку.

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

Второе слагаемое "compute bound" относится к проблемам, когда количество инструкций велико по сравнению с количеством чтения/записи в памяти. Например, рассмотрим матричное умножение: количество инструкций будет O (n ^ 3), когда n - размер матрицы. В этом случае можно ожидать, что GPU превзойдет процессор с определенным размером матрицы. Другим примером может быть то, что многие сложные тригонометрические вычисления (синус/косинус и т.д.) Выполняются на "нескольких" элементах данных.

Как правило: вы можете предположить, что чтение/запись одного элемента данных из "основной" памяти графического процессора имеет латентность около 500 инструкций....

Следовательно, еще одним ключевым моментом для производительности графических процессоров является локальность данных. Если вам нужно читать или записывать данные (и в большинстве случаев вам придется;-)), тогда вы должны убедитесь, что данные хранятся как можно ближе к ядрам GPU. Таким образом, графические процессоры имеют определенные области памяти (называемые "локальной памятью" или "разделяемой памятью" ), которые обычно имеют размер всего лишь несколько КБ, но особенно эффективны для данных, которые собираются участвовать в вычислении.

Итак, чтобы еще раз подчеркнуть это: программирование на GPU - это искусство, которое только отдаленно связано с параллельным программированием на процессоре. Такие вещи, как Threads на Java, со всей инфраструктурой concurrency, например ThreadPoolExecutors, ForkJoinPools и т.д., Могут создать впечатление, что вам просто нужно как-то разделить вашу работу и распределить ее между несколькими процессорами. На GPU вы можете столкнуться с проблемами на гораздо более низком уровне: занятость, давление в регистре, общее давление памяти, совместное использование памяти... просто назвать несколько.

Однако, когда у вас есть проблема с параллельной параллелью, проблема с вычислением, GPU - это путь.


Общее замечание: Вы специально попросили CUDA. Но я настоятельно рекомендую вам также взглянуть на OpenCL. Он имеет несколько преимуществ. Прежде всего, это независимый от производителя открытый отраслевой стандарт, и есть реализация OpenCL от AMD, Apple, Intel и NVIDIA. Кроме того, в среде Java есть гораздо более широкая поддержка OpenCL. Единственный случай, когда я лучше соглашаюсь на CUDA, - это когда вы хотите использовать библиотеки времени исполнения CUDA, такие как CUFFT для БПФ или 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. Он предлагает специальные интерфейсы, которые могут быть реализованы, чтобы указать, что определенный класс должен быть выполнен на графическом процессоре. В отличие от Aparapi, он пытается автоматически сериализовать "релевантные" данные (то есть всю соответствующую часть графического объекта!) В представление, подходящее для графического процессора.

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

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, который затем может быть скомпилирован и выполнен на графическом процессоре. Однако библиотека, похоже, не является общедоступной.

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: низкоуровневые привязки, которые являются сопоставлением 1:1 исходного OpenCL API

http://jcuda.org/: привязки Java для CUDA: привязки низкого уровня, которые являются отображением 1:1 исходного API CUDA

Разное

http://sourceforge.net/projects/jopencl/: привязки Java для OpenCL. Похоже, что с 2010 года не будет поддерживаться

http://www.hoopoe-cloud.com/: привязки Java для CUDA. Кажется, что больше не поддерживается


Ответ 2

Я бы начал использовать один из проектов для Java и CUDA: http://www.jcuda.org/