CUDA (Nvidia GPU)와 함께 Java 사용


144

Java로 수행되는 비즈니스 프로젝트를 진행 중이며 비즈니스 시장을 계산하려면 엄청난 계산 능력이 필요합니다. 간단한 수학이지만 많은 양의 데이터가 있습니다.

우리는 그것을 시도하기 위해 일부 CUDA GPU를 주문했으며 CUDA는 Java를 지원하지 않기 때문에 어디서부터 시작해야하는지 궁금합니다. JNI 인터페이스를 빌드해야합니까? JCUDA를 사용해야합니까 아니면 다른 방법이 있습니까?

나는이 분야에 경험이 없으며 누군가가 나를 연구하고 배우기 시작할 수 있도록 무언가를 지시 해 줄 수 있기를 바랍니다.


2
GPU는 특정 유형의 컴퓨팅 집약적 문제를 가속화하는 데 도움이됩니다. 그러나 방대한 양의 데이터가있는 경우 IO에 바인딩 될 가능성이 높습니다. 대부분의 GPU는 해결책이 아닙니다.
steve cook

1
"GPGPU를 사용하여 Java 성능 향상"-> arxiv.org/abs/1508.06791
BlackBear

4
공개 질문의 일종, Marco13의 답변이 엄청나게 도움이되기 때문에 개조가 종료하지 않은 것이 기쁩니다! 위키 IMHO이어야합니다
JimLohse

답변:


443

우선, CUDA가 자동 계산을 더 빠르게하지 않을 것이라는 사실을 알고 있어야합니다. GPU 프로그래밍은 예술이기 때문에 한편으로, 그것은 매우 그걸 얻기 위해 도전, 매우 될 수있다 권리 . 반면에 GPU는 특정 종류 의 계산 에만 적합하기 때문 입니다.

기본적으로 GPU에서 모든 것을 계산할 수 있기 때문에 혼동 될 수 있습니다 . 물론 핵심은 좋은 속도 향상 여부를 결정하는 것입니다. 여기서 가장 중요한 분류는 문제가 작업 병렬 인지 데이터 병렬인지 입니다. 첫 번째는 대략적으로 말하면 여러 스레드가 자체 작업을 다소 독립적으로 수행하는 문제를 나타냅니다. 두 번째는 많은 스레드가 모두 동일 하지만 데이터의 다른 부분에서 수행하는 문제를 나타냅니다 .

후자는 GPU가 잘하는 종류의 문제입니다. GPU에는 많은 코어가 있으며 모든 코어는 동일하지만 입력 데이터의 다른 부분에서 작동합니다.

당신은 "간단한 수학이지만 엄청난 양의 데이터"를 가지고 있다고 언급했습니다. 이것은 완벽하게 데이터 병렬 문제처럼 들리므로 GPU에 적합하다고 생각할 수도 있지만 고려해야 할 또 다른 측면이 있습니다 .GPU는 이론적 계산 능력 (FLOPS, 초당 부동 소수점 연산) 측면에서 엄청나게 빠릅니다. 그러나 이들은 종종 메모리 대역폭에 의해 제한됩니다.

이것은 또 다른 분류 문제로 이어진다. 즉, 문제가 메모리 바운드 인지 계산 바운드 인지 여부 입니다.

첫 번째는 각 데이터 요소에 대해 수행되는 명령 수가 적은 문제를 나타냅니다. 예를 들어, 병렬 벡터 덧셈을 고려하십시오. 두 개의 데이터 요소 를 읽은 다음 한 번의 덧셈을 수행 한 다음 합계를 결과 벡터에 써야 합니다. 단일 추가는 메모리 읽기 / 쓰기 노력을 보상하지 않기 때문에 GPU에서이 작업을 수행 할 때 속도가 향상되지 않습니다.

두 번째 용어 인 "컴퓨 트 바운드"는 메모리 읽기 / 쓰기 수에 비해 명령어 수가 많은 문제를 나타냅니다. 예를 들어, 행렬 곱셈을 고려하십시오. n이 행렬의 크기 인 경우 명령어 수는 O (n ^ 3)입니다. 이 경우 GPU가 특정 매트릭스 크기에서 CPU보다 성능이 우수 할 것으로 예상 할 수 있습니다. 또 다른 예는 "복수의"데이터 요소에 대해 많은 복잡한 삼각법 계산 (사인 / 코사인 등)이 수행되는 경우입니다.

경험적으로 "메인"GPU 메모리에서 하나의 데이터 요소를 읽거나 쓰는 데 약 500 개의 명령 대기 시간이 있다고 가정 할 수 있습니다 ....

따라서 GPU 성능의 또 다른 핵심 요점은 데이터 지역성입니다 . 데이터 를 읽거나 써야하는 경우 (대부분의 경우 ;-)해야 함) 데이터를 최대한 가깝게 유지하십시오 GPU 코어에 가능합니다. 따라서 GPU는 일반적으로 크기가 몇 KB에 불과하지만 계산에 관련된 데이터에 특히 효율적인 특정 메모리 영역 ( "로컬 메모리"또는 "공유 메모리"라고 함)을 갖습니다.

다시 강조하자면 : GPU 프로그래밍은 예술이며 CPU에서의 병렬 프로그래밍과 만 관련이 있습니다. 같은 모든 동시성 인프라 자바 스레드 같은 것들, ThreadPoolExecutors, ForkJoinPools등 그냥 어떻게 든 작업을 분할하고 여러 프로세서들에게 배포해야한다는 인상을 줄 수 있습니다. GPU에서는 점유, 등록 압력, 공유 메모리 압력, 메모리 통합 등과 같은 훨씬 낮은 수준의 문제가 발생할 수 있습니다.

그러나 해결해야 할 데이터 병렬 컴퓨팅 바운드 문제가있는 경우 GPU를 사용하는 것이 좋습니다.


일반적인 언급 : CUDA를 구체적으로 요청했습니다. 그러나 OpenCL도 살펴 보는 것이 좋습니다. 몇 가지 장점이 있습니다. 우선, 벤더 독립적 인 개방형 산업 표준이며 AMD, Apple, Intel 및 NVIDIA의 OpenCL 구현이 있습니다. 또한 Java 세계에서 OpenCL에 대한 훨씬 광범위한 지원이 있습니다. CUDA에 대해 해결하려는 유일한 경우는 FFT 용 CUFFT 또는 BLAS (매트릭스 / 벡터 연산) 용 CUBLAS와 같은 CUDA 런타임 라이브러리를 사용하려는 경우입니다. OpenCL에 유사한 라이브러리를 제공하는 방법이 있지만 이러한 라이브러리에 대한 고유 한 JNI 바인딩을 작성하지 않으면 Java 측에서 직접 사용할 수 없습니다.


또한 2012 년 10 월 OpenJDK HotSpot 그룹이 "Sumatra"프로젝트를 시작했다고 들었습니다 : http://openjdk.java.net/projects/sumatra/ . 이 프로젝트의 목표는 JVM에서 직접 GPU 지원을 제공 하고 JIT의 지원을 제공하는 것입니다. 현재 상태와 첫 번째 결과는 http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev 의 메일 링리스트에서 확인할 수 있습니다 .


그러나 얼마 전 저는 일반적으로 "Java on the GPU"와 관련된 리소스를 수집했습니다. 여기서는 특별한 순서없이 여기에 다시 요약하겠습니다.

( 면책 조항 : 저는 http://jcuda.org/http://jocl.org/ 의 저자입니다 )

(바이트) 코드 변환 및 OpenCL 코드 생성 :

https://github.com/aparapi/aparapi : AMD가 만들고 적극적으로 관리하는 오픈 소스 라이브러리입니다. 특수한 "Kernel"클래스에서는 병렬로 실행해야하는 특정 메소드를 대체 할 수 있습니다. 이 메소드의 바이트 코드는 런타임에 자체 바이트 코드 리더를 사용하여로드됩니다. 코드는 OpenCL 코드로 변환 된 다음 OpenCL 컴파일러를 사용하여 컴파일됩니다. 그런 다음 GPU 또는 CPU 일 수있는 OpenCL 장치에서 결과를 실행할 수 있습니다. OpenCL로 컴파일 할 수 없거나 OpenCL을 사용할 수없는 경우에도 스레드 풀을 사용하여 코드가 병렬로 실행됩니다.

https://github.com/pcpratts/rootbeer1 : Java의 일부를 CUDA 프로그램으로 변환하기위한 오픈 소스 라이브러리. 특정 클래스가 GPU에서 실행되어야 함을 나타 내기 위해 구현 될 수있는 전용 인터페이스를 제공합니다. Aparapi와 달리 "관련"데이터 (즉, 객체 그래프의 전체 관련 부분!)를 GPU에 적합한 표현으로 자동 직렬화하려고 시도합니다.

https://code.google.com/archive/p/java-gpu/ : 주석이 달린 Java 코드 (일부 제한이 있음)를 CUDA 코드로 변환하기위한 라이브러리이며,이 코드는 GPU에서 코드를 실행하는 라이브러리로 컴파일됩니다. 라이브러리는 PhD 논문의 맥락에서 개발되었으며 번역 프로세스에 대한 심도있는 배경 정보가 포함되어 있습니다.

https://github.com/ochafik/ScalaCL : OpenCL에 대한 스칼라 바인딩. 특수 스칼라 컬렉션을 OpenCL과 병렬로 처리 할 수 ​​있습니다. 컬렉션의 요소에서 호출되는 함수는 일반적인 스칼라 함수 (일부 제한 있음) 일 수 있으며 OpenCL 커널로 변환됩니다.

언어 확장

http://www.ateji.com/px/index.html : 병렬 구조 (예 : 병렬 for 루프, OpenMP 스타일)를 허용하는 Java 용 언어 확장으로 OpenCL을 사용하여 GPU에서 실행됩니다. 불행히도이 유망한 프로젝트는 더 이상 유지되지 않습니다.

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 : CUDA 백엔드가있는 OpenMP 구성을위한 Java 언어 확장

Java OpenCL / CUDA 바인딩 라이브러리

https://github.com/ochafik/JavaCL : OpenCL에 대한 Java 바인딩 : 자동 생성 된 저수준 바인딩을 기반으로하는 객체 지향 OpenCL 라이브러리

http://jogamp.org/jocl/www/ : OpenCL에 대한 Java 바인딩 : 자동 생성 된 저수준 바인딩을 기반으로하는 객체 지향 OpenCL 라이브러리

http://www.lwjgl.org/ : OpenCL을위한 Java 바인딩 : 자동 생성 된 저수준 바인딩 및 객체 지향 편의 클래스

http://jocl.org/ : OpenCL에 대한 Java 바인딩 : 원래 OpenCL API의 1 : 1 맵핑 인 저수준 바인딩

http://jcuda.org/ : CUDA를위한 Java 바인딩 : 원래 CUDA API의 1 : 1 매핑 인 저수준 바인딩

여러 가지 잡다한

http://sourceforge.net/projects/jopencl/ : OpenCL에 대한 Java 바인딩. 2010 년 이후 더 이상 유지되지 않는 것 같습니다

http://www.hoopoe-cloud.com/ : CUDA를위한 Java 바인딩. 더 이상 유지되지 않는 것 같습니다



2 개의 행렬을 더하고 세 번째 행렬에 결과를 저장하는 작업을 고려하십시오. OpenCL이없는 CPU에서 다중 스레드를 사용하면 병목 현상이 항상 추가 단계가됩니다. 이 작업은 분명히 데이터 병렬입니다. 그러나 미리 계산 바운드인지 메모리 바운드인지 알 수 없다고 가정 해 봅시다. 구현하는 데 많은 시간과 리소스가 필요하며이 작업을 수행하는 데 CPU가 훨씬 더 좋습니다. 그렇다면 OpenCL 코드를 구현하지 않고 어떻게 이것을 사전에 식별합니까?
Cool_Coder

2
@Cool_Coder 실제로 특정 작업이 GPU 구현으로 혜택을 볼 수 있는지 여부를 미리 말하기는 어렵습니다. 첫 번째 직감을 느끼기 위해서는 아마도 다른 유스 케이스에 대한 경험이 필요할 것입니다 (실제로는 그렇지 않습니다). 첫 번째 단계는 nvidia.com/object/cuda_showcase_html.html 을보고 "유사한"문제가 있는지 확인하는 것입니다. (CUDA이지만 개념적으로 OpenCL과 매우 가깝기 때문에 대부분의 경우 결과를 전송할 수 있습니다). 대부분의 경우 속도 향상에 대해서도 언급하고 있으며, 그 중 많은 부분이 논문 또는 코드와 연결되어 있습니다
Marco13

aparapi의 경우 +1-Java에서 opencl을 시작하는 간단한 방법이며 간단한 경우 CPU와 GPU 성능을 쉽게 비교할 수 있습니다. 또한 AMD에서 유지 관리하지만 Nvidia 카드와 잘 작동합니다.
steve cook

12
이것은 내가 지금까지 본 최고의 답변 중 하나입니다. 시간과 노력에 감사드립니다!
ViggyNash

1
@AlexPunnen 이것은 아마도 주석의 범위를 벗어납니다. 내가 아는 한 OpenCV는 docs.opencv.org/2.4/modules/gpu/doc/introduction.html에서 CUDA를 지원 합니다. developer.nvidia.com/npp이 편리 할 수있는 많은 화상 처리 루틴을 갖는다. 그리고 github.com/GPUOpen-ProfessionalCompute-Tools/HIP 는 CUDA의 "대체"일 수 있습니다. 이 질문을 새로운 질문으로하는 것이 가능할 수도 있지만, "의견 기반"/ "타사 라이브러리 요청"에 대한
공감대


2

내가 한 연구 에서 Nvidia GPU를 대상으로하고 OpenCL 대신 CUDA를 사용하기로 결정한 경우 Java에서 CUDA API를 사용하는 세 가지 방법을 찾았습니다.

  1. JCuda (또는 대체) - http://www.jcuda.org/ . 이것은 내가 작업중 인 문제에 대한 최상의 솔루션 인 것 같습니다. CUBLAS와 같은 많은 라이브러리가 JCuda에서 사용 가능합니다. 커널은 여전히 ​​C로 작성됩니다.
  2. JNI-JNI 인터페이스는 제가 가장 좋아하는 글이 아니지만 매우 강력하며 CUDA로 할 수있는 모든 것을 할 수 있습니다.
  3. JavaCPP-기본적으로 C 코드를 직접 작성하지 않고도 Java로 JNI 인터페이스를 작성할 수 있습니다. 여기에 예가 있습니다 : Java에서 CUDA 코드 작업을 실행하는 가장 쉬운 방법은 무엇입니까?CUDA 스러스트와 함께 사용하는 방법에 대한 나에게 이것은 JNI 인터페이스를 작성하는 것 같습니다.

이 모든 대답은 기본적으로 Java에서 C / C ++ 코드를 사용하는 방법입니다. Java를 사용해야하는 이유와 C / C ++에서 Java를 사용할 수없는 경우 스스로에게 물어보십시오.

Java를 좋아하고 그것을 사용하는 방법을 알고 있고 모든 포인터 관리 및 C / C ++와 함께 제공되는 기능을 사용하고 싶지 않다면 JCuda가 아마도 답일 것입니다. 반면에 CUDA Thrust 라이브러리 및 이와 같은 다른 라이브러리는 C / C ++에서 많은 포인터 관리를 수행하는 데 사용될 수 있으며 아마도 그 점을 살펴 봐야 할 것입니다.

C / C ++를 좋아하고 포인터 관리를 신경 쓰지 않지만 Java를 사용하도록 강요하는 다른 제약 조건이있는 경우 JNI가 최선의 방법 일 수 있습니다. JNI 메소드가 커널 명령의 래퍼가 될 경우 JCuda를 사용할 수도 있습니다.

Cuda4J 및 Root Beer와 같은 JCuda에 대한 몇 가지 대안이 있지만 유지되지 않는 것 같습니다. 이 JCuda는 작성 당시 CUDA 10.1을 지원합니다. 최신 CUDA SDK입니다.

또한 deeplearning4j 및 Hadoop과 같이 CUDA를 사용하는 몇 가지 Java 라이브러리가 있으므로 커널 코드를 직접 작성하지 않고도 원하는 작업을 수행 할 수 있습니다. 나는 그들을 너무 많이 보지 않았다.


1

Marco13은 이미 훌륭한 답변을 제공했습니다. .

CUDA / OpenCL 커널을 구현하지 않고 GPU를 사용하는 방법을 찾고 있다면 finmath-lib-cuda-extensions (finmath-lib-gpu-extensions) http : // finmath 에 대한 참조를 추가하고 싶습니다 .net / finmath-lib-cuda-extensions / (면책 조항 : 저는이 프로젝트의 관리자입니다).

이 프로젝트는 "벡터 클래스"의 구현을 제공하기 위해이라는 인터페이스 RandomVariable를 제공합니다.이 인터페이스 는 산술 연산 및 벡터 축소를 제공합니다. CPU 및 GPU에 대한 구현이 있습니다. 알고리즘 차별화 또는 일반 평가를 사용하는 구현이 있습니다.

GPU의 성능 향상은 현재 작지만 크기가 100.000 인 벡터의 경우 10보다 큰 성능 향상을 얻을 수 있습니다. 커널 크기가 작기 때문입니다. 향후 버전에서 개선 될 것입니다.

GPU 구현은 JCuda 및 JOCL을 사용하며 Nvidia 및 ATI GPU에서 사용할 수 있습니다.

라이브러리는 Apache 2.0이며 Maven Central을 통해 사용 가능합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.