Sử dụng Java với GPU Nvidia (CUDA)


144

Tôi đang làm việc trên một dự án kinh doanh được thực hiện bằng Java và nó cần sức mạnh tính toán rất lớn để tính toán thị trường kinh doanh. Toán đơn giản, nhưng với lượng dữ liệu khổng lồ.

Chúng tôi đã đặt hàng một số GPU CUDA để dùng thử và vì Java không được CUDA hỗ trợ, tôi tự hỏi nên bắt đầu từ đâu. Tôi có nên xây dựng giao diện JNI không? Tôi có nên sử dụng JCUDA hay có những cách khác?

Tôi không có kinh nghiệm trong lĩnh vực này và tôi muốn nếu ai đó có thể hướng tôi đến một cái gì đó để tôi có thể bắt đầu nghiên cứu và học hỏi.


2
GPU sẽ giúp bạn tăng tốc các loại vấn đề chuyên sâu tính toán cụ thể. Tuy nhiên, nếu bạn có một lượng dữ liệu khổng lồ, nhiều khả năng bạn sẽ bị ràng buộc IO. Nhiều khả năng GPU không phải là giải pháp.
steve cook

1
"Tăng hiệu suất Java bằng GPGPUs" -> arxiv.org/abs/1508.06791
BlackBear

4
Là một câu hỏi mở, tôi rất vui vì các mod đã không tắt nó vì câu trả lời từ Marco13 rất hữu ích! Nên là wiki IMHO
JimLohse

Câu trả lời:


442

Trước hết, bạn nên biết rằng CUDA sẽ không tự động làm cho việc tính toán nhanh hơn. Một mặt, bởi vì lập trình GPU là một nghệ thuật, và nó có thể rất, rất khó khăn để làm cho nó đúng . Mặt khác, vì GPU chỉ phù hợp với một số loại tính toán nhất định .

Điều này nghe có vẻ khó hiểu, vì về cơ bản bạn có thể tính toán mọi thứ trên GPU. Điểm mấu chốt là, tất nhiên, liệu bạn sẽ đạt được tốc độ tốt hay không. Phân loại quan trọng nhất ở đây là liệu một vấn đề là song song nhiệm vụ hay dữ liệu song song . Đầu tiên, nói một cách đại khái, về các vấn đề trong đó một số luồng đang thực hiện các nhiệm vụ của riêng chúng, ít nhiều độc lập. Cái thứ hai đề cập đến các vấn đề trong đó nhiều luồng đều làm giống nhau - nhưng trên các phần khác nhau của dữ liệu.

Vấn đề thứ hai là loại GPU rất tốt: Chúng có nhiều lõi và tất cả các lõi đều làm như vậy, nhưng hoạt động trên các phần khác nhau của dữ liệu đầu vào.

Bạn đã đề cập rằng bạn có "toán đơn giản nhưng với lượng dữ liệu khổng lồ". Mặc dù điều này có vẻ như là một vấn đề song song dữ liệu hoàn hảo và do đó nó rất phù hợp với GPU, nhưng có một khía cạnh khác cần xem xét: GPU rất nhanh về mặt sức mạnh tính toán lý thuyết (FLOPS, Floating Point Operations Per Second). Nhưng chúng thường bị giảm xuống bởi băng thông bộ nhớ.

Điều này dẫn đến một phân loại khác của vấn đề. Cụ thể là các vấn đề bị ràng buộc bộ nhớ hoặc tính toán ràng buộc .

Cái đầu tiên đề cập đến các vấn đề trong đó số lượng hướng dẫn được thực hiện cho từng thành phần dữ liệu thấp. Ví dụ, hãy xem xét một phép cộng vectơ song song: Bạn sẽ phải đọc hai phần tử dữ liệu, sau đó thực hiện một phép cộng duy nhất và sau đó viết tổng vào vectơ kết quả. Bạn sẽ không thấy tăng tốc khi thực hiện việc này trên GPU, bởi vì việc bổ sung duy nhất không bù đắp cho những nỗ lực đọc / ghi bộ nhớ.

Thuật ngữ thứ hai, "tính toán ràng buộc", đề cập đến các vấn đề trong đó số lượng hướng dẫn cao so với số lượng bộ nhớ đọc / ghi. Ví dụ, hãy xem xét phép nhân ma trận: Số lượng lệnh sẽ là O (n ^ 3) khi n là kích thước của ma trận. Trong trường hợp này, người ta có thể mong đợi rằng GPU sẽ vượt trội hơn CPU ở một kích thước ma trận nhất định. Một ví dụ khác có thể là khi nhiều phép tính lượng giác phức tạp (sin / cosine, v.v.) được thực hiện trên các phần tử dữ liệu "vài".

Theo nguyên tắc thông thường: Bạn có thể giả sử rằng việc đọc / ghi một yếu tố dữ liệu từ bộ nhớ GPU "chính" có độ trễ khoảng 500 hướng dẫn ....

Do đó, một điểm quan trọng khác đối với hiệu suất của GPU là địa phương dữ liệu : Nếu bạn phải đọc hoặc ghi dữ liệu (và trong hầu hết các trường hợp, bạn sẽ phải ;-)), thì bạn nên đảm bảo rằng dữ liệu được giữ gần như là có thể cho các lõi GPU. Do đó, GPU có các vùng bộ nhớ nhất định (được gọi là "bộ nhớ cục bộ" hoặc "bộ nhớ dùng chung") thường chỉ có kích thước vài KB, nhưng đặc biệt hiệu quả đối với dữ liệu sắp được tính toán.

Vì vậy, để nhấn mạnh điều này một lần nữa: lập trình GPU là một nghệ thuật, điều đó chỉ liên quan từ xa đến lập trình song song trên CPU. Những thứ như Chủ đề trong Java, với tất cả các cơ sở hạ tầng đồng thời như ThreadPoolExecutors, ForkJoinPoolsv.v. có thể mang lại cảm giác rằng bạn chỉ cần phân chia công việc của mình bằng cách nào đó và phân phối nó giữa một số bộ xử lý. Trên GPU, bạn có thể gặp các thử thách ở cấp độ thấp hơn nhiều: Chiếm dụng, áp lực đăng ký, áp lực bộ nhớ chia sẻ, kết hợp bộ nhớ ... chỉ để đặt tên cho một số ít.

Tuy nhiên, khi bạn có một vấn đề song song về dữ liệu, tính toán ràng buộc để giải quyết, GPU là cách để giải quyết.


Một nhận xét chung: Yêu cầu cụ thể của bạn cho CUDA. Nhưng tôi thực sự khuyên bạn cũng nên xem OpenCL. Nó có một số lợi thế. Trước hết, đó là một tiêu chuẩn công nghiệp mở, độc lập với nhà cung cấp và có các triển khai OpenCL của AMD, Apple, Intel và NVIDIA. Ngoài ra, có một hỗ trợ rộng lớn hơn nhiều cho OpenCL trong thế giới Java. Trường hợp duy nhất mà tôi muốn giải quyết cho CUDA là khi bạn muốn sử dụng các thư viện thời gian chạy CUDA, như CUFFT cho FFT hoặc CUBLAS cho BLAS (hoạt động Ma trận / Vector). Mặc dù có các cách tiếp cận để cung cấp các thư viện tương tự cho OpenCL, nhưng chúng không thể được sử dụng trực tiếp từ phía Java, trừ khi bạn tạo các ràng buộc JNI của riêng mình cho các thư viện này.


Bạn cũng có thể thấy thú vị khi biết rằng vào tháng 10 năm 2012, nhóm HotSpot OpenJDK đã bắt đầu dự án "Sumatra": http://openjdk.java.net/projects/sumatra/ . Mục tiêu của dự án này là cung cấp hỗ trợ GPU trực tiếp trong JVM, với sự hỗ trợ từ JIT. Tình trạng hiện tại và kết quả đầu tiên có thể được nhìn thấy trong danh sách gửi thư của họ tại http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev


Tuy nhiên, cách đây một thời gian, tôi đã thu thập một số tài nguyên liên quan đến "Java trên GPU" nói chung. Tôi sẽ tóm tắt những điều này một lần nữa ở đây, không theo thứ tự cụ thể.

( Tuyên bố miễn trừ trách nhiệm : Tôi là tác giả của http://jcuda.org/http://jocl.org/ )

(Byte) dịch mã và tạo mã OpenCL:

https://github.com/aparapi/aparapi : Một thư viện mã nguồn mở được AMD tạo ra và duy trì tích cực. Trong một lớp "Kernel" đặc biệt, người ta có thể ghi đè một phương thức cụ thể cần được thực thi song song. Mã byte của phương thức này được tải trong thời gian chạy bằng cách sử dụng trình đọc mã byte riêng. Mã được dịch sang mã OpenCL, sau đó được biên dịch bằng trình biên dịch OpenCL. Kết quả sau đó có thể được thực thi trên thiết bị OpenCL, có thể là GPU hoặc CPU. Nếu không thể biên dịch thành OpenCL (hoặc không có OpenCL), mã sẽ vẫn được thực thi song song, sử dụng Thread Pool.

https://github.com/pcpratts/rootbeer1 : Một thư viện mã nguồn mở để chuyển đổi các phần của Java thành các chương trình CUDA. Nó cung cấp các giao diện chuyên dụng có thể được triển khai để chỉ ra rằng một lớp nhất định sẽ được thực thi trên GPU. Trái ngược với Aparapi, nó cố gắng tự động tuần tự hóa dữ liệu "có liên quan" (nghĩa là phần hoàn toàn có liên quan của biểu đồ đối tượng!) Thành một đại diện phù hợp với GPU.

https://code.google.com.vn/archive/p/java-gpu/ : Một thư viện để dịch mã Java chú thích (với một số hạn chế) thành mã CUDA, sau đó được biên dịch thành thư viện thực thi mã trên GPU. Thư viện được phát triển trong bối cảnh luận án tiến sĩ, trong đó có thông tin cơ bản sâu sắc về quá trình dịch thuật.

https://github.com/ochafik/ScalaCL : Các ràng buộc Scala cho OpenCL. Cho phép các bộ sưu tập Scala đặc biệt được xử lý song song với OpenCL. Các hàm được gọi trên các phần tử của các bộ sưu tập có thể là các hàm Scala thông thường (với một số hạn chế) sau đó được dịch sang các nhân OpenCL.

Phần mở rộng ngôn ngữ

http://www.ateji.com/px/index.html : Một phần mở rộng ngôn ngữ cho Java cho phép các cấu trúc song song (ví dụ: song song cho các vòng lặp, kiểu OpenMP) sau đó được thực thi trên GPU với OpenCL. Thật không may, dự án rất hứa hẹn này không còn được duy trì.

http://www.habanero.rice.edu/Publications.html (JCUDA): Một thư viện có thể dịch Mã Java đặc biệt (được gọi là mã JCUDA) sang mã Java- và CUDA-C, sau đó có thể được biên dịch và thực thi trên GPU. Tuy nhiên, thư viện dường như không được công khai.

https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html : Phần mở rộng ngôn ngữ Java cho các cấu trúc OpenMP, với phụ trợ CUDA

Các thư viện liên kết Java OpenCL / CUDA

https://github.com/ochafik/JavaCL : Các ràng buộc Java cho OpenCL: Thư viện OpenCL hướng đối tượng, dựa trên các ràng buộc cấp thấp được tạo tự động

http://jogamp.org/jocl/www/ : Các ràng buộc Java cho OpenCL: Thư viện OpenCL hướng đối tượng, dựa trên các ràng buộc cấp thấp được tạo tự động

http://www.lwjgl.org/ : Các ràng buộc Java cho OpenCL: Các ràng buộc mức độ thấp được tạo tự động và các lớp tiện lợi hướng đối tượng

http://jocl.org/ : Các ràng buộc Java cho OpenCL: Các ràng buộc cấp thấp là ánh xạ 1: 1 của API OpenCL gốc

http://jcuda.org/ : Các ràng buộc Java cho CUDA: Các ràng buộc cấp thấp là ánh xạ 1: 1 của API CUDA ban đầu

Điều khoản khác

http://sourceforge.net/projects/jopencl/ : Các ràng buộc Java cho OpenCL. Dường như không còn được duy trì kể từ năm 2010

http://www.hoopoe-cloud.com/ : Các ràng buộc Java cho CUDA. Dường như không còn được duy trì



xem xét một hoạt động thêm 2 ma trận và lưu trữ kết quả trong một ma trận thứ ba. Khi mutli luồng trên CPU mà không có OpenCL, nút cổ chai sẽ luôn là bước trong đó việc bổ sung xảy ra. Hoạt động này rõ ràng là dữ liệu song song. Nhưng hãy nói rằng chúng tôi không biết liệu nó sẽ được tính toán ràng buộc hay ràng buộc bộ nhớ trước đó. Phải mất rất nhiều thời gian và tài nguyên để thực hiện và sau đó thấy rằng CPU tốt hơn nhiều khi thực hiện thao tác này. Vì vậy, làm thế nào để xác định trước điều này mà không thực hiện mã OpenCL.
Cool_Coder

2
@Cool_Coder Thật sự rất khó để biết trước liệu (hoặc bao nhiêu) một tác vụ nhất định sẽ được hưởng lợi từ việc triển khai GPU. Đối với một cảm giác ruột đầu tiên, có lẽ người ta cần một số kinh nghiệm với các trường hợp sử dụng khác nhau (mà tôi thừa nhận cũng không thực sự có). Bước đầu tiên có thể là xem xét nvidia.com/object/cuda_showcase_html.html và xem liệu có vấn đề "tương tự" được liệt kê hay không. (Đó là CUDA, nhưng về mặt khái niệm rất gần với OpenCL mà kết quả có thể được chuyển trong hầu hết các trường hợp). Trong hầu hết các trường hợp, việc tăng tốc cũng được đề cập và nhiều người trong số họ có liên kết đến các giấy tờ hoặc thậm chí mã
Marco13

+1 cho aparapi - đây là cách đơn giản để bắt đầu với opencl trong java và cho phép bạn dễ dàng so sánh hiệu năng của CPU với GPU trong các trường hợp đơn giản. Ngoài ra, nó được AMD duy trì nhưng hoạt động tốt với các thẻ Nvidia.
steve cook

12
Đây là một trong những phản hồi tốt nhất tôi từng thấy trên StackOverflow. Cám ơn về thời gian và sự cố gắng!
ViggyNash

1
@AlexPunnen Điều này có lẽ nằm ngoài phạm vi của các bình luận. Theo như tôi biết, OpenCV có một số hỗ trợ CUDA, kể từ docs.opencv.org/2.4/modules/gpu/doc/int sinhtion.html . Nhà phát triển.nvidia.com / npp có nhiều quy trình xử lý hình ảnh, có thể tiện dụng. Và github.com/GPUOpen-Prof ProfessionCompute-Tools/HIP có thể là một "sự thay thế" cho CUDA. Có thể hỏi đây là một câu hỏi mới, nhưng người ta phải cẩn thận để phát âm đúng, để tránh bị hạ thấp vì "dựa trên ý kiến" / "yêu cầu thư viện của bên thứ ba" ...
Marco13


2

Từ nghiên cứu tôi đã thực hiện, nếu bạn đang nhắm mục tiêu GPU Nvidia và đã quyết định sử dụng CUDA qua OpenCL , tôi đã tìm thấy ba cách để sử dụng API CUDA trong java.

  1. JCuda (hoặc thay thế) - http://www.jcuda.org/ . Đây có vẻ như là giải pháp tốt nhất cho các vấn đề tôi đang làm việc. Nhiều thư viện như CUBLAS có sẵn trong JCuda. Kernels vẫn được viết bằng C mặc dù.
  2. Giao diện JNI - JNI không phải là sở thích của tôi để viết, nhưng rất mạnh mẽ và sẽ cho phép bạn làm bất cứ điều gì CUDA có thể làm.
  3. JavaCPP - Điều này về cơ bản cho phép bạn tạo giao diện JNI trong Java mà không cần viết mã C trực tiếp. Có một ví dụ ở đây: cách dễ nhất để chạy mã CUDA hoạt động trong Java là gì? về cách sử dụng cái này với lực đẩy CUDA. Đối với tôi, điều này có vẻ như bạn cũng có thể chỉ cần viết một giao diện JNI.

Tất cả các câu trả lời này về cơ bản chỉ là cách sử dụng mã C / C ++ trong Java. Bạn nên tự hỏi tại sao bạn cần sử dụng Java và nếu bạn không thể làm điều đó trong C / C ++ thay thế.

Nếu bạn thích Java và biết cách sử dụng nó và không muốn làm việc với tất cả quản lý con trỏ và những gì không đi kèm với C / C ++ thì JCuda có lẽ là câu trả lời. Mặt khác, thư viện CUDA Thrust và các thư viện khác giống như nó có thể được sử dụng để thực hiện nhiều thao tác quản lý con trỏ trong C / C ++ và có lẽ bạn nên xem xét điều đó.

Nếu bạn thích C / C ++ và không ngại quản lý con trỏ, nhưng có những ràng buộc khác buộc bạn phải sử dụng Java, thì JNI có thể là cách tiếp cận tốt nhất. Mặc dù vậy, nếu các phương thức JNI của bạn chỉ là các hàm bao cho các lệnh kernel, bạn cũng có thể sử dụng JCuda.

Có một vài lựa chọn thay thế cho JCuda như Cuda4J và Root Bia, nhưng những thứ đó dường như không được duy trì. Trong khi tại thời điểm viết bài này, JCuda hỗ trợ CUDA 10.1. đó là SDK CUDA cập nhật nhất.

Ngoài ra, có một vài thư viện java sử dụng CUDA, chẳng hạn như deeplearning4j và Hadoop, có thể thực hiện những gì bạn đang tìm kiếm mà không yêu cầu bạn phải viết mã hạt nhân trực tiếp. Tôi đã không nhìn vào họ quá nhiều mặc dù.


1

Marco13 đã cung cấp một câu trả lời tuyệt vời .

Trong trường hợp bạn đang tìm kiếm cách sử dụng GPU mà không triển khai hạt nhân CUDA / OpenCL, tôi muốn thêm một tham chiếu đến finmath-lib-cuda-extend (finmath-lib-gpu- extend ) http: // finmath .net / finmath-lib-cuda-extend / (từ chối trách nhiệm: Tôi là người duy trì dự án này).

Dự án cung cấp một triển khai "các lớp vectơ", chính xác là một giao diện được gọi RandomVariable, cung cấp các phép toán số học và giảm trên các vectơ. Có các triển khai cho CPU và GPU. Có triển khai sử dụng phân biệt thuật toán hoặc định giá đơn giản.

Các cải tiến hiệu suất trên GPU hiện tại rất nhỏ (nhưng đối với các vectơ có kích thước 100.000, bạn có thể nhận được hệ số> 10 cải tiến hiệu suất). Điều này là do kích thước hạt nhân nhỏ. Điều này sẽ cải thiện trong một phiên bản trong tương lai.

Việc triển khai GPU sử dụng JCuda và JOCL và có sẵn cho GPU Nvidia và ATI.

Thư viện là Apache 2.0 và có sẵn thông qua Maven Central.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.