Прежде всего, вы должны знать тот факт, что CUDA не будет автоматически ускорять вычисления. С одной стороны, потому что программирование на GPU - это искусство, и сделать его правильно может быть очень и очень сложно . С другой стороны, потому что графические процессоры хорошо подходят только для определенных видов вычислений.
Это может показаться сбивающим с толку, потому что вы можете вычислить что угодно на GPU. Ключевым моментом, конечно же, является то, добьетесь ли вы хорошего ускорения или нет. Наиболее важной классификацией здесь является то, является ли проблема параллельной задачей или параллельными данными . Первый относится, грубо говоря, к задачам, в которых несколько потоков работают над своими задачами более или менее независимо. Второй относится к проблемам, при которых многие потоки делают одно и то же, но с разными частями данных.
Последнее - это проблема, с которой хорошо справляются графические процессоры: у них много ядер, и все ядра делают то же самое, но работают с разными частями входных данных.
Вы упомянули, что у вас «простая математика, но с огромным объемом данных». Хотя это может звучать как проблема идеального параллелизма данных и, следовательно, как будто она хорошо подходит для графического процессора, есть еще один аспект, который следует учитывать: графические процессоры смехотворно быстры с точки зрения теоретической вычислительной мощности (FLOPS, операций с плавающей запятой в секунду). Но они часто ограничиваются пропускной способностью памяти.
Это приводит к другой классификации проблем. А именно, связаны ли проблемы с памятью или вычислениями .
Первый относится к проблемам, в которых количество инструкций, выполняемых для каждого элемента данных, невелико. Например, рассмотрим сложение параллельного вектора: вам нужно будет прочитать два элемента данных, затем выполнить одно сложение, а затем записать сумму в вектор результатов. Вы не увидите ускорения при выполнении этого на графическом процессоре, потому что одно добавление не компенсирует усилия по чтению / записи памяти.
Второй термин, «ограничение вычислений», относится к проблемам, в которых количество инструкций велико по сравнению с количеством операций чтения / записи в память. Например, рассмотрим матричное умножение: количество инструкций будет O (n ^ 3), когда n - размер матрицы. В этом случае можно ожидать, что GPU будет превосходить CPU при определенном размере матрицы. Другой пример может быть, когда многие сложные тригонометрические вычисления (синус / косинус и т. Д.) Выполняются для «небольшого количества» элементов данных.
Практическое правило: вы можете предположить, что чтение / запись одного элемента данных из «основной» памяти графического процессора имеет задержку около 500 инструкций ....
Следовательно, еще одним ключевым моментом для производительности графических процессоров является локальность данных : если вам нужно читать или записывать данные (а в большинстве случаев вам придется ;-)), вы должны убедиться, что данные хранятся как можно ближе к возможно для ядер графического процессора. Таким образом, графические процессоры имеют определенные области памяти (называемые «локальной памятью» или «общей памятью»), размер которых обычно составляет всего несколько КБ, но они особенно эффективны для данных, которые должны быть задействованы в вычислениях.
Итак, чтобы подчеркнуть это еще раз: программирование на GPU - это искусство, которое только удаленно связано с параллельным программированием на CPU. Такие вещи , как потоки в Java, со всем параллелизмом инфраструктурой , как ThreadPoolExecutors
, и ForkJoinPools
т.д. , могут создать впечатление , что вы просто должны разделить свою работу как - то и распределить его между несколькими процессорами. На графическом процессоре вы можете столкнуться с проблемами на гораздо более низком уровне: загруженность, давление регистров, давление общей памяти, объединение памяти ... и это лишь некоторые из них.
Однако, когда вам нужно решить задачу, связанную с параллельными данными и привязкой к вычислениям, лучше всего подойдет графический процессор.
Общее замечание: вы специально просили CUDA. Но я настоятельно рекомендую вам также взглянуть на OpenCL. У него есть несколько преимуществ. Прежде всего, это открытый отраслевой стандарт, не зависящий от производителя, и есть реализации OpenCL от AMD, Apple, Intel и NVIDIA. Кроме того, в мире Java существует гораздо более широкая поддержка OpenCL. Единственный случай, когда я предпочел бы согласиться на CUDA, - это когда вы хотите использовать библиотеки времени выполнения CUDA, такие как CUFFT для FFT или CUBLAS для BLAS (операции с матрицами / вектором). Хотя существуют подходы для предоставления аналогичных библиотек для OpenCL, их нельзя напрямую использовать со стороны Java, если вы не создадите свои собственные привязки JNI для этих библиотек.
Возможно, вам будет интересно узнать, что в октябре 2012 года группа OpenJDK HotSpot начала проект «Суматра»: http://openjdk.java.net/projects/sumatra/ . Цель этого проекта - обеспечить поддержку графического процессора непосредственно в 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. В специальном классе «Kernel» можно переопределить конкретный метод, который должен выполняться параллельно. Байт-код этого метода загружается во время выполнения с помощью собственного считывателя байт-кода. Код переводится в код OpenCL, который затем компилируется с помощью компилятора OpenCL. Затем результат может быть выполнен на устройстве OpenCL, которое может быть графическим процессором или процессором. Если компиляция в OpenCL невозможна (или OpenCL недоступен), код все равно будет выполняться параллельно с использованием пула потоков.
https://github.com/pcpratts/rootbeer1 : библиотека с открытым исходным кодом для преобразования частей Java в программы CUDA. Он предлагает выделенные интерфейсы, которые могут быть реализованы, чтобы указать, что определенный класс должен выполняться на графическом процессоре. В отличие от Aparapi, он пытается автоматически сериализовать «релевантные» данные (то есть всю соответствующую часть графа объекта!) В представление, подходящее для GPU.
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, которое позволяет создавать параллельные конструкции (например, параллельные циклы for, стиль OpenMP), которые затем выполняются на графическом процессоре с помощью 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: низкоуровневые привязки, которые представляют собой сопоставление 1: 1 с исходным API OpenCL.
http://jcuda.org/ : привязки Java для CUDA: привязки низкого уровня, которые являются отображением 1: 1 исходного API CUDA
Разное
http://sourceforge.net/projects/jopencl/ : привязки Java для OpenCL. Похоже, не обслуживается с 2010 года.
http://www.hoopoe-cloud.com/ : привязки Java для CUDA. Кажется, больше не поддерживается