การใช้ Java กับ Nvidia GPUs (CUDA)


144

ฉันกำลังทำงานในโครงการธุรกิจที่ทำใน Java และต้องการพลังการคำนวณขนาดใหญ่เพื่อคำนวณตลาดธุรกิจ คณิตศาสตร์ง่าย ๆ แต่มีข้อมูลจำนวนมหาศาล

เราสั่งให้ CUDA GPUs ลองด้วยและเนื่องจาก Java ไม่รองรับ CUDA ฉันจึงสงสัยว่าจะเริ่มต้นอย่างไร ฉันควรสร้างอินเตอร์เฟส JNI หรือไม่ ฉันควรใช้ JCUDA หรือมีวิธีอื่นหรือไม่?

ฉันไม่มีประสบการณ์ในสาขานี้และฉันต้องการถ้ามีคนสามารถชี้แนะทางให้ฉันเพื่อให้ฉันสามารถเริ่มค้นคว้าและเรียนรู้ได้


2
GPUs จะช่วยให้คุณเร่งความเร็วในการคำนวณปัญหาที่เฉพาะเจาะจงมากขึ้น อย่างไรก็ตามหากคุณมีข้อมูลจำนวนมากคุณมีแนวโน้มที่จะถูกผูกไว้กับ IO GPUs ส่วนใหญ่ไม่ใช่ทางออก
สตีฟทำอาหาร

1
"การเพิ่มประสิทธิภาพของ Java ด้วย GPGPUs" -> arxiv.org/abs/1508.06791
BlackBear

4
เป็นคำถามเปิดฉันดีใจที่ mods ไม่ปิดเพราะคำตอบจาก Marco13 มีประโยชน์อย่างไม่น่าเชื่อ! ควรเป็นวิกิ IMHO
JimLohse

คำตอบ:


443

ก่อนอื่นคุณควรตระหนักถึงความจริงที่ว่า CUDA จะไม่ทำให้การคำนวณเร็วขึ้นโดยอัตโนมัติ ในมือข้างหนึ่งเพราะการเขียนโปรแกรม GPU เป็นศิลปะและมันสามารถมากที่ท้าทายมากที่จะได้รับมันขวา ในทางกลับกันเนื่องจาก GPU นั้นเหมาะสำหรับการคำนวณบางประเภทเท่านั้น

สิ่งนี้อาจฟังดูสับสนเนื่องจากคุณสามารถคำนวณอะไรก็ได้บน GPU จุดสำคัญคือแน่นอนว่าคุณจะประสบความสำเร็จในการเร่งความเร็วหรือไม่ การจัดหมวดหมู่ที่สำคัญที่สุดคือว่านี่เป็นปัญหางานคู่ขนานหรือข้อมูลแบบขนาน คนแรกอ้างถึงการพูดอย่างคร่าว ๆ ถึงปัญหาที่หลายกระทู้กำลังทำงานของตนเองไม่มากก็น้อย ส่วนที่สองหมายถึงปัญหาที่เธรดจำนวนมากกำลังทำสิ่งเดียวกัน - แต่ในส่วนต่าง ๆ ของข้อมูล

หลังเป็นปัญหาที่ GPUs ทำได้ดี: มีหลายคอร์และคอร์ทุกตัวทำเหมือนกัน แต่ทำงานในส่วนต่าง ๆ ของข้อมูลอินพุท

คุณพูดถึงว่าคุณมี "คณิตศาสตร์ง่าย ๆ แต่มีข้อมูลจำนวนมาก" แม้ว่าสิ่งนี้อาจฟังดูเป็นปัญหาที่ขนานกันอย่างสมบูรณ์แบบของข้อมูลและดังนั้นจึงเหมาะสำหรับ GPU แต่ก็มีอีกด้านที่ต้องพิจารณา: GPUs มีความรวดเร็วอย่างน่าขันในแง่ของพลังการคำนวณเชิงทฤษฎี (FLOPS, Floating Point Operations Per Second) แต่บ่อยครั้งที่พวกเขาถูกแบนด์วิดท์หน่วยความจำลง

สิ่งนี้นำไปสู่การจำแนกปัญหาอีกประเภทหนึ่ง กล่าวคือไม่ว่าจะเป็นปัญหาที่หน่วยความจำที่ถูกผูกไว้หรือการคำนวณที่ถูกผูกไว้

คนแรกหมายถึงปัญหาที่จำนวนคำสั่งที่ทำสำหรับแต่ละองค์ประกอบข้อมูลต่ำ ตัวอย่างเช่นพิจารณาการเพิ่มเวกเตอร์แบบขนาน: คุณจะต้องอ่านองค์ประกอบข้อมูลสองรายการจากนั้นทำการบวกแบบครั้งเดียวแล้วเขียนผลรวมลงในเวกเตอร์ผลลัพธ์ คุณจะไม่เห็นการเร่งความเร็วเมื่อทำสิ่งนี้กับ GPU เพราะการเพิ่มครั้งเดียวไม่ได้ชดเชยความพยายามในการอ่าน / เขียนหน่วยความจำ

คำศัพท์ที่สอง "ขอบเขตการคำนวณ" หมายถึงปัญหาที่จำนวนคำสั่งนั้นสูงเมื่อเทียบกับจำนวนหน่วยความจำที่อ่าน / เขียน ตัวอย่างเช่นพิจารณาการคูณเมทริกซ์: จำนวนคำสั่งจะเป็น O (n ^ 3) เมื่อ n คือขนาดของเมทริกซ์ ในกรณีนี้เราสามารถคาดหวังได้ว่า GPU จะมีประสิทธิภาพสูงกว่าซีพียูในขนาดเมทริกซ์ที่แน่นอน อีกตัวอย่างหนึ่งคือเมื่อมีการคำนวณตรีโกณมิติเชิงซ้อนจำนวนมาก (ไซน์ / โคไซน์ ฯลฯ ) ในองค์ประกอบข้อมูล "น้อย"

ตามกฎทั่วไป: คุณสามารถสันนิษฐานได้ว่าการอ่าน / เขียนข้อมูลหนึ่งองค์ประกอบจากหน่วยความจำ GPU "หลัก" มีความล่าช้าประมาณ 500 คำสั่ง ....

ดังนั้นจุดสำคัญอีกประการหนึ่งสำหรับประสิทธิภาพของ GPU คือตำแหน่งข้อมูล : หากคุณต้องอ่านหรือเขียนข้อมูล (และในกรณีส่วนใหญ่คุณจะต้อง ;-)) คุณควรตรวจสอบให้แน่ใจว่าข้อมูลนั้นถูกเก็บไว้ใกล้เคียงที่สุด เป็นไปได้ที่จะแกน GPU GPU จึงมีพื้นที่หน่วยความจำบางอย่าง (เรียกว่า "หน่วยความจำท้องถิ่น" หรือ "หน่วยความจำที่ใช้ร่วมกัน") ซึ่งโดยปกติจะมีขนาดเพียงไม่กี่ KB แต่มีประสิทธิภาพโดยเฉพาะอย่างยิ่งสำหรับข้อมูลที่กำลังจะมีส่วนร่วมในการคำนวณ

ดังนั้นเพื่อเน้นสิ่งนี้อีกครั้ง: การเขียนโปรแกรม GPU เป็นศิลปะที่เกี่ยวข้องกับการเขียนโปรแกรมแบบขนานบน CPU จากระยะไกลเท่านั้น สิ่งที่ชอบกระทู้ใน Java มีทั้งหมดเห็นพ้องด้วยโครงสร้างพื้นฐานเช่นThreadPoolExecutors, ForkJoinPoolsฯลฯ อาจจะให้ความประทับใจที่คุณเพียงแค่ต้องแยกการทำงานของคุณอย่างใดและแจกจ่ายในหมู่โปรเซสเซอร์หลาย บน GPU คุณอาจพบกับความท้าทายในระดับที่ต่ำกว่ามากเช่นการเข้าพักการลงทะเบียนความกดดันการแชร์หน่วยความจำการรวมหน่วยความจำ ... เพื่อชื่อไม่กี่คน

อย่างไรก็ตามเมื่อคุณมีปัญหาข้อมูลแบบขนาน, ขอบเขตการคำนวณเพื่อแก้ไข GPU เป็นวิธีที่จะไป


หมายเหตุทั่วไป: คุณขอเฉพาะ CUDA แต่ฉันขอแนะนำให้คุณดู OpenCL ด้วย มันมีข้อดีหลายประการ ก่อนอื่นมันเป็นมาตรฐานของอุตสาหกรรมที่เปิดอิสระและมีการใช้งาน OpenCL โดย AMD, Apple, Intel และ NVIDIA นอกจากนี้ยังมีการสนับสนุนที่กว้างขวางยิ่งขึ้นสำหรับ OpenCL ในโลก Java กรณีเดียวที่ฉันต้องการชำระ CUDA คือเมื่อคุณต้องการใช้ไลบรารีรันไทม์ CUDA เช่น CUFFT สำหรับ FFT หรือ CUBLAS สำหรับ BLAS (การดำเนินการ Matrix / Vector) แม้ว่าจะมีวิธีการในการจัดหาไลบรารี่ที่คล้ายกันสำหรับ 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/ )

(Byte) การแปลโค้ดและการสร้างโค้ด OpenCL:

https://github.com/aparapi/aparapi : ไลบรารีโอเพ่นซอร์สที่สร้างและดูแลโดย AMD ในคลาส "เคอร์เนล" พิเศษหนึ่งสามารถแทนที่วิธีการเฉพาะซึ่งควรจะดำเนินการในแบบคู่ขนาน โค้ดไบต์ของวิธีนี้ถูกโหลดเมื่อรันไทม์โดยใช้ตัวอ่าน bytecode ของตัวเอง โค้ดถูกแปลเป็นโค้ด OpenCL ซึ่งจะถูกคอมไพล์โดยใช้คอมไพเลอร์ OpenCL ผลลัพธ์สามารถดำเนินการได้บนอุปกรณ์ OpenCL ซึ่งอาจเป็น GPU หรือ CPU หากการรวบรวมเป็น OpenCL เป็นไปไม่ได้ (หรือไม่มี OpenCL) รหัสจะยังคงถูกดำเนินการแบบขนานโดยใช้กลุ่มเธรด

https://github.com/pcpratts/rootbeer1 : ไลบรารีโอเพ่นซอร์สสำหรับการแปลงส่วนต่าง ๆ ของ Java เป็นโปรแกรม CUDA มันมีอินเทอร์เฟซเฉพาะที่อาจนำไปใช้เพื่อบ่งชี้ว่าคลาสบางอย่างควรดำเนินการบน GPU ตรงกันข้ามกับ Aparapi จะพยายามเรียงลำดับข้อมูล "ที่เกี่ยวข้อง" โดยอัตโนมัติ (นั่นคือส่วนที่เกี่ยวข้องอย่างสมบูรณ์ของกราฟวัตถุ!) ไปสู่การแสดงที่เหมาะสำหรับ GPU

https://code.google.com/archive/p/java-gpu/ : ห้องสมุดสำหรับการแปลโค้ด Java ที่มีคำอธิบายประกอบ (มีข้อ จำกัด บางอย่าง) เป็นรหัส CUDA ซึ่งจะถูกรวบรวมไว้ในไลบรารีที่ประมวลผลโค้ดบน GPU ห้องสมุดได้รับการพัฒนาในบริบทของวิทยานิพนธ์ระดับปริญญาเอกซึ่งมีข้อมูลพื้นฐานที่ลึกซึ้งเกี่ยวกับกระบวนการแปล

https://github.com/ochafik/ScalaCL : การผูกสกาล่าสำหรับ OpenCL อนุญาตให้ประมวลผลคอลเลกชันสกาล่าแบบพิเศษพร้อมกับ 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 ซึ่งสามารถรวบรวมและดำเนินการใน 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 ของ OpenCL API ดั้งเดิม

http://jcuda.org/ : การรวม Java สำหรับ CUDA: การเชื่อมระดับต่ำที่เป็นการแมป 1: 1 ของ CUDA API ดั้งเดิม

เบ็ดเตล็ด

http://sourceforge.net/projects/jopencl/ : การรวม Java สำหรับ OpenCL ดูเหมือนจะไม่ได้รับการบำรุงรักษาอีกต่อไปตั้งแต่ปี 2010

http://www.hoopoe-cloud.com/ : การรวม Java สำหรับ CUDA ดูเหมือนจะไม่ได้รับการบำรุงรักษาอีกต่อไป



พิจารณาการดำเนินการของการเพิ่ม 2 เมทริกซ์และจัดเก็บผลลัพธ์ในเมทริกซ์ที่สาม เมื่อ mutli เธรดบน CPU โดยไม่มี OpenCL คอขวดจะเป็นขั้นตอนที่การเพิ่มเกิดขึ้นเสมอ การดำเนินการนี้เห็นได้ชัดว่าเป็นข้อมูลแบบขนาน แต่ให้บอกว่าเราไม่รู้ว่ามันจะถูกคำนวณขอบเขตหรือหน่วยความจำที่ถูกผูกไว้ล่วงหน้า ต้องใช้เวลาและทรัพยากรในการดำเนินการแล้วดูว่า CPU ทำงานได้ดีกว่ามาก ดังนั้นวิธีหนึ่งระบุก่อนนี้โดยไม่ต้องใช้รหัส OpenCL
Cool_Coder

2
@Cool_Coder แน่นอนว่าเป็นการยากที่จะบอกล่วงหน้าว่างานบางอย่างจะได้รับประโยชน์จากการใช้ GPU หรือไม่ สำหรับความรู้สึกแรกของลำไส้เราอาจต้องการประสบการณ์กับกรณีการใช้งานที่แตกต่างกัน (ซึ่งฉันยอมรับว่ายังไม่มี) ขั้นตอนแรกคือดูที่nvidia.com/object/cuda_showcase_html.htmlและดูว่ามีปัญหา "คล้ายกัน" ที่ระบุไว้หรือไม่ (เป็น CUDA แต่เป็นแนวคิดใกล้เคียงกับ OpenCL ที่ผลลัพธ์สามารถโอนในกรณีส่วนใหญ่) ในกรณีส่วนใหญ่ความเร็วดังกล่าวยังถูกกล่าวถึงและหลายคนมีลิงก์ไปยังเอกสารหรือรหัส
Marco13

+1 สำหรับ aparapi - เป็นวิธีที่ง่ายในการเริ่มต้นกับ opencl ใน java และช่วยให้คุณเปรียบเทียบประสิทธิภาพของ CPU กับ GPU สำหรับกรณีง่าย ๆ ได้อย่างง่ายดาย นอกจากนี้ยังดูแลโดย AMD แต่ทำงานได้ดีกับการ์ด Nvidia
สตีฟทำอาหาร

12
นี่เป็นหนึ่งในคำตอบที่ดีที่สุดที่ฉันเคยเห็นใน StackOverflow ขอบคุณสำหรับเวลาและความพยายาม!
ViggyNash

1
@AlexPunnen นี่อาจเกินขอบเขตของความคิดเห็น เท่าที่ผมรู้ว่ามีบางส่วน OpenCV สนับสนุน CUDA เป็นของdocs.opencv.org/2.4/modules/gpu/doc/introduction.html developer.nvidia.com/nppมีขั้นตอนการประมวลผลภาพจำนวนมากซึ่งอาจจะเป็นประโยชน์ และgithub.com/GPUOpen-ProfessionalCompute-Tools/HIPอาจเป็น "ทางเลือก" สำหรับ CUDA มันอาจจะเป็นไปได้ที่จะถามว่านี่เป็นคำถามใหม่ แต่อย่างหนึ่งที่จะต้องมีความระมัดระวังในการวลีมันอย่างถูกต้องเพื่อหลีกเลี่ยงการ downvotes สำหรับ "ความเห็นตาม" / "ขอให้ห้องสมุดของบุคคลที่สาม" ...
Marco13


2

จากการวิจัยที่ฉันทำถ้าคุณกำลังกำหนดเป้าหมาย Nvidia GPUs และตัดสินใจใช้ CUDA ผ่านOpenCLฉันพบสามวิธีในการใช้ CUDA API ใน java

  1. JCuda (หรือทางเลือก) - http://www.jcuda.org/ ดูเหมือนว่าทางออกที่ดีที่สุดสำหรับปัญหาที่ฉันกำลังทำอยู่ ห้องสมุดหลายแห่งเช่น CUBLAS มีอยู่ใน JCuda เมล็ดยังเขียนเป็นภาษาซีอยู่ดี
  2. JNI - อินเตอร์เฟส JNI ไม่ใช่สิ่งที่ฉันชอบในการเขียน แต่มีประสิทธิภาพมากและอนุญาตให้คุณทำทุกสิ่งที่ CUDA ทำได้
  3. JavaCPP - สิ่งนี้ช่วยให้คุณสร้างอินเตอร์เฟส JNI ใน Java โดยไม่ต้องเขียนโค้ด C โดยตรง มีตัวอย่างที่นี่: วิธีที่ง่ายที่สุดในการเรียกใช้รหัส CUDA ทำงานใน Java คืออะไร? ของวิธีการใช้สิ่งนี้ด้วยแรงผลักของ CUDA สำหรับฉันดูเหมือนว่าคุณอาจเพิ่งเขียนอินเตอร์เฟส JNI

คำตอบทั้งหมดเหล่านี้เป็นเพียงวิธีการใช้รหัส C / C ++ ใน Java คุณควรถามตัวเองว่าทำไมคุณต้องใช้ Java และถ้าคุณไม่สามารถทำได้ใน C / C ++ แทน

หากคุณชอบ Java และรู้วิธีใช้งานและไม่ต้องการทำงานกับการจัดการตัวชี้ทั้งหมดและอะไรที่ไม่ได้มาพร้อมกับ C / C ++ ดังนั้น JCuda น่าจะเป็นคำตอบ ในทางกลับกัน, CUDA Thrust library และไลบรารี่อื่น ๆ เช่นนั้นสามารถใช้ในการจัดการพอยน์เตอร์จำนวนมากใน C / C ++ และบางทีคุณควรดูที่

หากคุณชอบ C / C ++ และไม่สนใจการจัดการตัวชี้ แต่มีข้อ จำกัด อื่น ๆ ที่บังคับให้คุณใช้ Java ดังนั้น JNI อาจเป็นวิธีที่ดีที่สุด แม้ว่าหากวิธีการ JNI ของคุณเป็นเพียงคำสั่งล้อมสำหรับคำสั่งเคอร์เนลคุณก็อาจใช้ JCuda เช่นกัน

มีทางเลือกเล็กน้อยสำหรับ JCuda เช่น Cuda4J และ Root Beer แต่ดูเหมือนว่าจะไม่ได้รับการบำรุงรักษา ขณะที่เวลาเขียน JCuda นี้รองรับ CUDA 10.1 ซึ่งเป็น CUDA SDK ที่ทันสมัยที่สุด

นอกจากนี้ยังมีไลบรารี java บางตัวที่ใช้ CUDA เช่น deeplearning4j และ Hadoop ซึ่งอาจทำสิ่งที่คุณต้องการโดยไม่ต้องเขียนโค้ดเคอร์เนลโดยตรง ฉันไม่ได้ดูพวกเขามากเกินไป


1

Marco13 ให้คำตอบที่ยอดเยี่ยมแล้ว

ในกรณีที่คุณกำลังค้นหาวิธีการใช้ GPU โดยไม่ใช้เคอร์เนล CUDA / OpenCL ฉันต้องการเพิ่มการอ้างอิงถึง 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.