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
, ForkJoinPools
v.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/ và 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ì