10 คะแนน โดย GN⁺ 2025-07-27 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • มีการเผยแพร่เดโมโปรเจ็กต์ที่ทำให้โค้ดเบสเดียวที่เขียนด้วย Rust สามารถทำงานได้บนทุกแพลตฟอร์ม GPU และ CPU หลัก ๆ เช่น CUDA, Vulkan(SPIR-V), Metal, DirectX 12, WebGPU, CPU
  • การเขียนโปรแกรม GPU แบบเดิมมีความซับซ้อนและเกิดปัญหาโค้ดซ้ำจากการต้องใช้ ภาษาแยกอย่าง GLSL, HLSL เป็นต้น แต่เดโมนี้รองรับทุกเป้าหมาย GPU ได้ด้วย โค้ด Rust ล้วนเท่านั้น
  • มีการผสานโปรเจ็กต์หลักอย่าง Rust GPU(SPIR-V), Rust CUDA(NVVM IR), Naga(ชั้นแปลงภาษา GPU) เข้าด้วยกัน ทำให้อัลกอริทึม bitonic sort เดียวกันทำงานได้ทั้งบน CPU และ GPU ทุกแพลตฟอร์ม และฟีเจอร์ภาษาของ Rust อย่าง no_std, conditional compilation, Newtype, Enum, Trait ก็ยังใช้กับโค้ด GPU ได้เช่นเดิม
  • แม้ยังเหลืองานปรับปรุงในด้าน การรวมเข้ากับ rustc อย่างเป็นทางการ, การดีบัก, ความสม่ำเสมอของ API เป็นต้น แต่ก็นับเป็น จุดเปลี่ยนของการประมวลผลข้ามแพลตฟอร์มบน GPU

การทำให้การประมวลผลข้ามแพลตฟอร์มบน GPU ด้วย Rust เกิดขึ้นจริง

แนะนำโปรเจ็กต์และความสำคัญ

  • โค้ดเบส Rust ชุดเดียว สามารถรันเคอร์เนลเดียวกันได้บน CUDA(NVIDIA), Vulkan(SPIR-V), Metal(Apple), DirectX 12(Windows), WebGPU(เบราว์เซอร์), CPU เป็นต้น
  • โดยไม่ต้องใช้ภาษาเฉพาะสำหรับ shader หรือ kernel (เช่น GLSL, HLSL) ก็สามารถประมวลผลแบบเดียวกันบน GPU และ CPU ได้ด้วย โค้ด Rust ล้วน
  • สิ่งนี้ช่วยลดความซ้ำซ้อนของโค้ดและความซับซ้อนระหว่าง GPU·CPU ได้อย่างมาก และนำข้อดีของภาษาและเครื่องมือในระบบนิเวศ Rust (เช่น type safety, การทดสอบ, เอกสาร, การจัดการบิลด์) มาใช้กับการเขียนโปรแกรม GPU ได้ด้วย

ที่มา

  • การเขียนโปรแกรม GPU แบบดั้งเดิมจำเป็นต้องใช้ภาษา shader ที่เฉพาะกับแต่ละแพลตฟอร์ม (เช่น GSL, HLSL, MSL, WGSL เป็นต้น)
  • ผลที่ตามมาคือโค้ด CPU และ GPU ถูกแยกออกจากกัน เกิดปัญหา logic ซ้ำและเพิ่มความซับซ้อนในการพัฒนา
  • เพื่อรับมือกับปัญหานี้ ชุมชน Rust จึงผลักดันแนวทาง คอมไพล์ Rust ทั่วไปให้ไปยังเป้าหมาย GPU
    • Rust GPU: คอมไพล์โค้ด Rust เป็น SPIR-V เพื่อทำงานบน Vulkan และ GPU ที่รองรับ SPIR-V
    • Rust CUDA: คอมไพล์โค้ด Rust เป็น IR สำหรับ NVIDIA CUDA (NVVM IR, PTX) เพื่อรันบน CUDA
    • Naga: เลเยอร์กลางที่รองรับการแปลงระหว่างภาษา GPU หลายแบบ (WGSL, SPIR-V, GLSL, MSL, HLSL) โดยใช้งานหลักในโปรเจ็กต์ wgpu และรับผิดชอบด้านการพกพาข้ามแบ็กเอนด์
  • แต่ละโปรเจ็กต์เริ่มต้นอย่างอิสระและมี API กับโครงสร้างต่างกัน ทว่าจากความร่วมมือในช่วงหลัง ก็ทำให้การรัน GPU จาก โค้ดเบสร่วมกัน เกิดขึ้นจริงได้

วิธีการพัฒนา

  • ในเดโมตัวอย่าง มีการเขียน อัลกอริทึม bitonic sort ด้วยโค้ด Rust ชุดเดียว และใช้โค้ดเดียวกันรันได้กับทุกเป้าหมายทั้ง GPU และ CPU
  • คำศัพท์สำคัญของคอมโพเนนต์
    • Host: โค้ด Rust ที่รันบน CPU (จุดเริ่มงาน)
    • Device: GPU/CPU ที่เคอร์เนลรันอยู่จริง
    • Driver API: API ระดับล่างสำหรับสื่อสารกับอุปกรณ์ เช่น CUDA, Vulkan, Metal, DX12
    • Backend: เลเยอร์ abstraction ใน Rust สำหรับ Driver API เป้าหมาย (เช่น cust, ash, wgpu)
  • สามารถเลือกแบ็กเอนด์และควบคุมการบิลด์ได้ผ่านการผสมกันของ feature flag และ target ของ Rust
    • ตัวอย่างเช่น รองรับตัวเลือกบิลด์แยกตามแบ็กเอนด์อย่าง wgpu, ash, cuda
    • สามารถสร้างหลายแบ็กเอนด์ไว้ในไบนารีเดียว และเลือกใช้งานแบบไดนามิกตอนรันไทม์ได้

ลำดับการคอมไพล์เคอร์เนล

  • ตาม target และ feature จะมีการสร้างไบนารีสำหรับรันบน GPU (เช่น SPIR-V, PTX) ระหว่างการบิลด์ และฝังไว้ใน object file
  • ตอนรันไทม์จะโหลดเคอร์เนลที่ฝังไว้ แล้วแปลงเป็นฟอร์แมตที่แพลตฟอร์มต้องการผ่าน Naga เป็นต้น ก่อนนำไปรัน
  • ตัวอย่าง:
    • macOS: แปลง SPIR-V เป็น MSL → รันบน Metal
    • Windows: แปลง SPIR-V เป็น HLSL → รันบน DX12
    • Linux/Android: SPIR-V → รันบน Vulkan
    • CUDA: คอมไพล์ PTX เป็น SASS แล้วอัปโหลดขึ้น GPU เพื่อรัน

เทคนิคการเขียนโค้ด GPU ที่เป็นมิตรกับ Rust

  • รองรับ no_std

    • GPU ไม่มีการรองรับ OS ดังนั้นการใช้ no_std ของ Rust จึงเป็นสิ่งจำเป็น
    • เนื่องจากระบบนิเวศพื้นฐานของ Rust รองรับสภาพแวดล้อมไร้ OS อย่าง embedded, firmware, kernel อยู่แล้ว GPU จึงสามารถรองรับได้ตามแนวทางมาตรฐานของ Rust โดยไม่ต้องมี “โหมดพิเศษ” แยกต่างหาก
  • conditional compilation

    • การใช้ cfg attributes ร่วมกับ feature flag ช่วยให้เขียนโค้ดแยกแพลตฟอร์มและแยก GPU/CPU ได้อย่างชัดเจนและกระชับ
    • IDE และคอมไพเลอร์เข้าใจเส้นทางโค้ดทั้งหมดได้ จึงช่วยเพิ่มความน่าเชื่อถือและประสิทธิภาพการพัฒนา
  • การใช้ newtype

    • ป้องกันข้อผิดพลาดระดับ type จากการแมปดัชนีหรือโครงสร้างโดยนัยที่อาจผิดพลาดบน GPU ได้ด้วย newtype
    • ใช้ #[repr(transparent)] เพื่อสร้าง abstraction ของ type โดยแทบไม่มี overhead จริง
  • Enum และการแทนค่าที่ปลอดภัย

    • ใช้ Rust Enum แทน magic number และใช้ #[repr(u32)] เพื่อให้แมปกับจำนวนเต็มแบบเนทีฟได้
    • pattern matching และ exhaustiveness (การจัดการทุกกรณีของการแตกแขนง) ช่วยให้ประกอบโค้ดได้อย่างปลอดภัย
    • แต่เมื่อมีการส่งค่า Enum ผ่าน shared buffer ระหว่าง CPU-GPU ต้องระวังให้ทุกค่าที่ส่งมาเป็น Enum ที่ถูกต้อง
  • อัลกอริทึม generic บนพื้นฐาน Trait

    • ใช้ Trait เพื่อสร้าง abstraction ของการเปรียบเทียบ การแปลง และการคำนวณร่วมกันสำหรับชนิดค่าหลากหลายแบบในงาน GPU
    • ระบุ trait bound ของอัลกอริทึม generic ให้ชัดเจน เพื่อให้ได้ทั้ง type safety และประสิทธิภาพสูงสุด
  • #[inline] และการปรับแต่งประสิทธิภาพ

    • ใช้ #[inline] เพื่อผลักดันให้ชั้น abstraction หายไปจากผลลัพธ์การคอมไพล์จริง
    • ออกแบบให้ไม่มีต้นทุนจาก abstraction โดยคำนึงถึงลักษณะของ GPU เช่น ประสิทธิภาพและสแตกที่จำกัด
  • การประกอบ Struct และการจัดกลุ่มเชิงความหมาย

    • รวมพารามิเตอร์ GPU ที่ซับซ้อนเป็นหน่วยความหมายด้วย struct เพื่อเพิ่ม type safety และป้องกันจำนวนพารามิเตอร์ที่พุ่งสูงเกินไป
    • ใช้แพตเทิร์น smart constructor เพื่อป้องกันสถานะที่ไม่ถูกต้องตั้งแต่ขั้นคอมไพล์
  • โครงร่างหน่วยความจำและการควบคุมด้วย #[repr(C)]

    • เพื่อให้ข้อมูลเข้ากันได้กับ GPU จึงกำหนด layout ของ struct อย่างชัดเจนด้วย #[repr(C)]
    • ยังมีการกล่าวถึงความจำเป็นของการรองรับระดับภาษาที่ช่วยจัดการ padding ตาม GPU โดยอัตโนมัติในอนาคต
  • pattern matching

    • เป็นแนวคิดที่ขยายจาก switch/case ทำให้จัดการทุกเงื่อนไขและสถานะในโค้ด GPU ได้อย่างชัดเจน
    • คอมไพเลอร์สามารถตรวจสอบเส้นทางโค้ดและช่วยปรับแต่งประสิทธิภาพได้
  • ฟังก์ชัน generic

    • ใช้ตรรกะเดียวกันกับข้อมูลหลายชนิดได้โดยไม่ยึดติดกับ data type
    • trait bound และ monomorphization ช่วยเพิ่มความง่ายในการบำรุงรักษาและการทดสอบ
  • derive macro

    • สร้างการ implement trait ที่เหมาะกับ GPU โดยอัตโนมัติ เช่น Copy, Clone, Debug, PartialEq, Pod
    • ช่วยเพิ่มความปลอดภัยและลด boilerplate ของโค้ด
  • ระบบโมดูลและการจัดการ workspace

    • ใช้ระบบ package/module ของ Rust เพื่อจัดโครงสร้างซอร์สของ host code, kernel, type และแบ็กเอนด์แต่ละแบบ
    • Cargo workspace และ workspace dependency ช่วยให้ dependency ระหว่าง crate มีความสม่ำเสมอและดูแลรักษาได้สะดวก
  • การจัดรูปแบบ การ lint และการทำเอกสาร

    • สามารถจัดการโค้ด GPU ได้ด้วยเครื่องมือมาตรฐานของ Rust อย่าง rustfmt และ clippy ในแบบเดียวกันทั้งหมด เพื่อรักษาคุณภาพที่สม่ำเสมอ
    • ใช้ doc comment และ cargo doc เพื่อทำเอกสารให้โค้ด GPU ได้เช่นกัน
  • build script

    • ใช้ build.rs ของ Cargo เพื่อทำให้การบิลด์เคอร์เนลตาม feature flag และการฝังไบนารีเป็นอัตโนมัติ
  • unit test และประสิทธิภาพการพัฒนา

    • สามารถทดสอบโค้ด GPU kernel บน CPU ได้ด้วย จึงช่วยให้พัฒนาและตรวจหาบั๊กได้ง่าย
    • ใช้เครื่องมือดั้งเดิมอย่าง println debug, gdb/lldb ได้
    • แม้แต่ Vulkan kernel ก็ยังทดสอบบน CI ได้ผ่าน software driver (SwiftShader, lavapipe)
    • รองรับการทำงานร่วมกับเครื่องมือภายนอกอย่างการวัด code coverage และการทดสอบแบบ property-based ได้อย่างราบรื่น

ประสบการณ์นักพัฒนาที่ยังไม่สมบูรณ์และโจทย์ที่เหลือ

  • แบ็กเอนด์ GPU ยังไม่ได้รวมอยู่ในคอมไพเลอร์ Rust อย่างเป็นทางการ จึงต้องใช้ codegen backend แยกต่างหาก (spirv, nvvm) และต้องตรึงไว้กับ nightly เวอร์ชันหนึ่ง
  • เป้าหมาย CUDA ยังผูกกับ LLVM 7.1 ของ NVIDIA จึงต้องมีการบิลด์แยกบนลินุกซ์ดิสโทรรุ่นใหม่
  • ยังขาดประสบการณ์การบิลด์และดีบักเคอร์เนลที่ดี รวมถึงมีปัญหาเรื่องการไล่รอย error/debug info ที่ไม่เพียงพอ
  • API และชื่อของ standard library ระหว่าง Rust GPU กับ Rust CUDA ยังต่างกัน ทำให้เกิดความสับสน
  • ในระยะยาวยังจำเป็นต้องเสริมความสม่ำเสมอและการบูรณาการด้าน GPU ให้กับทั้งตัวภาษา Rust และระบบนิเวศโดยรวม

การมีส่วนร่วมของชุมชนและอนาคต

  • การรันโค้ดเดียวกันด้วย Rust บนทุกแพลตฟอร์ม GPU หลักเกิดขึ้นจริงแล้ว
  • ต่อจากนี้ งานที่ยังเหลือคือการปรับปรุงการบิลด์และดีบัก การขยายการรองรับในตัวภาษา Rust และ API รวมถึงการจูนประสิทธิภาพ
  • นักพัฒนาที่ต้องการเข้าร่วมและมีส่วนร่วมสามารถอ้างอิง GitHub repository ของ rust-gpu และ rust-cuda ได้

1 ความคิดเห็น

 
GN⁺ 2025-07-27
ความคิดเห็นบน Hacker News
  • น่าประทับใจมากที่เทคโนโลยีนี้ทำได้จริง
    แต่กรณีใช้งานของฉันคือการรันบนฮาร์ดแวร์ฝั่งไคลเอนต์แบบสุ่ม จึงไม่ค่อยไว้ใจทุกชั้นของ abstraction ที่สร้างครอบอยู่บน GPU API
    เป้าหมายคือการใช้ประโยชน์จากรายละเอียดระดับล่างของ GPU ให้ได้มากที่สุด และแนวทางที่มองว่ารายละเอียดพวกนี้เป็นเรื่องน่ารำคาญก็มักนำไปสู่บั๊กและประสิทธิภาพที่ลดลง
    เพราะแต่ละเป้าหมายมีความแตกต่างกันอย่างมีนัยสำคัญ
    ถ้าจะก้าวข้ามจุดนี้ได้ ฉันคิดว่าผู้ขายควรเป็นคนจัดหาระบบลักษณะนี้มาโดยตรง
    แต่เพราะจุดยืนระหว่างผู้ขายยังไม่ลงรอยกัน ก็เลยดูเหมือนว่าความต่างระหว่างแพลตฟอร์มยังสูงมาก
    บางกรณีก็มีข้อยกเว้นอย่าง Angle แต่กรณีแบบนี้ก็ได้เสถียรภาพมาด้วยการจำกัดฟีเจอร์เท่านั้น และสุดท้ายก็เสียประสิทธิภาพอยู่ดี
    ถึงอย่างนั้น การที่วิธีอย่าง conditional compilation ใช้งานได้ก็เป็นสิ่งที่ช่วยได้ชัดเจน

    • Rust เป็นภาษาเชิงระบบ จึงให้ระดับการควบคุมได้มากเท่าที่ต้องการ
      เราวางแผนจะสะท้อนรายละเอียดและ API ของ GPU เข้าไปในภาษาและไลบรารี core/std และเปิดเผยฟีเจอร์ของ GPU กับไดรเวอร์ผ่านระบบ cfg()
      (ผู้เขียน)

    • ฉันก็คิดเหมือนกัน
      ฉันระวังเสมอเวลาจะสร้างอะไรเชิงพาณิชย์บนชั้น abstraction, adapter หรือ conversion layer ที่ไม่รู้ว่าในอนาคตจะได้รับการสนับสนุนเพียงพอหรือไม่
      ใกล้จะถึงปี 2025 แล้ว แต่ก็ยังรู้สึกว่าจำเป็นอย่างยิ่งที่จะต้องมี open standard ที่รองรับโดยทุกผู้ขาย และเปิดให้ใช้ความสามารถทั้งหมดของฮาร์ดแวร์ GPU รุ่นใหม่ได้
      เมื่อสถานการณ์เป็นแบบนี้ แถม Nvidia ซึ่งสร้างกำแพงทางซอฟต์แวร์ที่แข็งแกร่งที่สุด ยังนั่งเป็นตัวแทนของ Khronos อีก ก็ยิ่งชวนให้คิด

    • ดูเหมือนคุณจะสนใจเรื่องประสิทธิภาพมาก เลยอยากถามด้วยความสงสัย
      สำหรับฉัน ภาพรวมของวงการ GPU ตอนนี้คล้ายกับ CPU ในอดีตมาก
      ฝั่ง CPU เคยมีโครงสร้างคอมไพเลอร์ 3 ชั้น โดยปรับแต่งที่ชั้นกลาง แล้วให้ชั้นสุดท้ายปล่อยโค้ดที่เหมาะกับฮาร์ดแวร์แต่ละแบบ
      ข้อดีของโครงสร้างนี้คือชั้น abstraction อยู่ได้นาน และตัวคอมไพเลอร์ฉลาดขึ้นเรื่อย ๆ ตามเวลา
      เลยสงสัยว่าฝั่ง GPU จะทำโครงสร้างแบบนี้ได้ไหม
      หรือความหลากหลายของ GPU มันมากเกินไปจนเป็นไปไม่ได้หรือไม่คุ้มค่า หรือจริง ๆ แล้วทิศทางก็ควรไปทางนั้นอยู่แล้วแต่ยังไปไม่ถึงด้านเทคนิค

    • เห็นด้วยเป๊ะ
      ฉันยังไม่ค่อยเห็นว่าการรัน Rust บน Nvidia GPU จะดีกว่าโค้ด CUDA เดิมอย่างไร
      เข้าใจเรื่องการเพิ่ม abstraction แต่สุดท้ายมันให้ความรู้สึกแบบ “ทำได้ทุกอย่างแบบปะปนกันไป”

    • จริง ๆ แล้วทุกอย่างก็คือ abstraction
      แม้แต่ CUDA เองก็เป็นเพียง abstraction ที่ทำให้ฮาร์ดแวร์ซึ่งต่างกันโดยสิ้นเชิงถูกมองเป็นภาพรวมเดียวกันในเชิงแนวคิด

  • ฉันพัฒนาแอปเสียงแบบเนทีฟ ซึ่งที่นี่ทุก cycle การคำนวณมีความสำคัญ
    อีกทั้งฉันต้องการ compute API แบบเต็ม ไม่ใช่แค่กราฟิกเชดเดอร์
    เลยสงสัยว่า pipeline แบบ "Rust -> WebGPU -> SPIR-V -> MSL -> Metal" นั้นแข็งแรงพอในเชิงประสิทธิภาพหรือไม่
    มันมีขั้นตอนแปลงหลายชั้นเกินไปจนดูเปราะและคาดเดายาก
    "... -> Vulkan -> MoltenVk -> ..." ก็เช่นกัน
    ในทางกลับกัน "Julia -> Metal" ข้าม MSL ไป และสามารถใช้ประโยชน์จากการปรับแต่งพิเศษของ Apple Silicon ได้โดยตรง (เช่น Unified Memory)
    นวัตกรรมที่นี่ไม่ใช่ภาษาเชดเดอร์ แต่เป็นการใช้ภาษาโปรแกรมเต็มรูปแบบอย่าง Rust
    Rust รองรับฟีเจอร์ต่าง ๆ เช่น newtype, trait, macro เป็นต้น

    • ตอนใช้ rust-gpu ไม่จำเป็นต้องผ่านชั้น WebGPU เสมอไป
      เพราะ rust-gpu เป็น codegen backend ของคอมไพเลอร์
      โครงสร้างของมันทำให้คอมไพล์ Rust MIR ไปเป็น SPIRV ได้โดยตรง

    • "Rust -> WebGPU -> SPIR-V -> MSL -> Metal" แข็งแรงพอในเชิงประสิทธิภาพไหม?
      โดยพื้นฐานแล้วมันคล้ายกับแนวคิดการ optimize ของ Apple ผ่าน Clang สำหรับ GPU
      SPIR-V เป็น representation ระดับกลางแบบเดียวกับ IR ที่ใช้ใน LLVM จึงสามารถ optimize แยกตามระบบได้
      ในทางทฤษฎี คุณสามารถใช้ code base เดียวเพื่อ target raster GPU ทุกตัวที่รองรับได้
      ส่วน stack แบบ Julia -> Metal จะพกพาได้น้อยกว่าเมื่อเทียบกัน
      สำหรับนักพัฒนาที่ทำงานจำกัดแพลตฟอร์มอย่างปลั๊กอินเสียงอาจไม่ใช่ประเด็น แต่ถ้าเป็นบริษัทพัฒนาข้ามแพลตฟอร์มอย่าง u-he หรือ Spectrasonics แล้ว pipeline ที่ซับซ้อนกว่าแต่ใช้ SPIR-V อาจน่าสนใจกว่า

    • ในด้านการคำนวณเชิงตัวเลขและการ optimize ต่อเนื่อง Julia เหมาะกว่าภาษาเชิงระบบอย่าง Rust มาก
      ถ้าดู compatibility matrix ของ Rust-CUDA จะเห็นว่าความต้องการใช้ Rust กับงาน CUDA มีน้อยมาก
      ฟีเจอร์ส่วนใหญ่ที่คนชอบใน CUDA ไม่มีอยู่ และถ้ามีความต้องการจริงก็น่าจะคืบหน้าไปมากกว่านี้แล้ว แต่ก็ไม่เป็นแบบนั้น
      ดูเหมือนโปรแกรมเมอร์ CUDA จะไม่ค่อยอยากใช้ Rust
      https://github.com/Rust-GPU/Rust-CUDA/blob/main/guide/src/features.md

  • แม้จะมีโค้ดที่ฉันเองก็อยากให้ไปรันบน GPU แต่ทุกอย่างในโลก GPU programming มันชวนปวดหัวจนสุดท้ายก็ไม่ทำ
    เลยคิดว่าประโยชน์ที่แท้จริงของ rust-gpu อาจเป็นการเปลี่ยนนักพัฒนา CPU ให้กลายเป็นนักพัฒนา GPU ได้ แม้จะต้องยอมแลกกับประสิทธิภาพบางส่วน
    ถ้าคุณชำนาญฝั่ง GPU อยู่แล้วและรู้ลึกเรื่อง cuda/vulkan/metal/dx ก็คงไม่รู้สึกว่ามันน่าสนใจมากนัก

  • ฉันเป็นแค่นักพัฒนาเว็บธรรมดา คำถามนี้อาจฟังดูงี่เง่าเพราะไม่เคยทำ GPU programming มาก่อน
    WebGPU เป็น API เดียวที่เข้ากันได้กับ backend ของ GPU ทั้งหมดไม่ใช่หรือ เลยสงสัยว่ามันแก้ปัญหาพวกนี้ไปหมดแล้วหรือเปล่า
    ดูเหมือน WebGPU จะเป็นหนึ่งใน backend ที่รองรับ ถ้าอย่างนั้นมันก็แปลว่าเอา abstraction ใหม่ไปครอบบน abstraction เดิม แล้วสุดท้ายก็ไปเรียก native GPU backend อยู่ดีไม่ใช่หรือ?

    • ไม่ใช่แบบนั้น
      WebGPU คือ API สำหรับควบคุม GPU จากฝั่ง CPU เพื่อให้ทำงานกราฟิกต่าง ๆ เช่นรันเชดเดอร์ คล้ายกับ D3D, Vulkan, SDL GPU
      ส่วน Rust-GPU คือภาษาสำหรับเขียนโค้ดเชดเดอร์ที่รันจริงบน GPU คล้าย HLSL, GLSL, WGSL

    • ตอนที่ Microsoft ยังมีอิทธิพลมาก เรามี directx
      แต่ตอนนี้ฉันไม่ค่อยรู้ว่าผู้ผลิต GPU แต่ละเจ้ากำลังทำ API เฉพาะสำหรับเทคโนโลยีของตัวเองมากน้อยแค่ไหน
      มีฟีเจอร์แปลกเฉพาะทางมากมายอย่าง DLSS, MFG, RTX
      ถ้าคุณเป็นตัวร้ายในการ์ตูน ก็อาจตั้งใจทำให้ API เดิมช้าลง แล้วทำ API ใหม่ที่เร็วกว่าและเป็นของผู้ขายรายนั้นให้เร็วอย่างเดียวก็ได้
      ยังไงฉันก็เป็นนักพัฒนาเว็บเหมือนกัน เลยไม่ได้รู้จริง แต่อย่างน้อย LLM ก็จะได้เรียนรู้เรื่องนี้ไปด้วย

    • ฉันมองว่า WebGPU ใกล้เคียงกับ minimum common API มากกว่า
      ตัวอย่างเช่น Zed editor เวอร์ชัน Mac จะ target Metal โดยตรง
      แล้วแม้แต่ความหมายของคำว่า "common" เองแต่ละคนก็เห็นไม่เหมือนกัน
      เหมือนกรณี OpenGL กับ Vulkan บริษัทที่มีอำนาจก็มักพยายามผลักดันให้ ecosystem ของตัวเองอย่าง CUDA, Metal, DirectX กลายเป็นมาตรฐานตลาด

    • ถ้ามันง่ายขนาดนั้นจริง CUDA ก็คงไม่กลายเป็นคูเมืองมหาศาลของ Nvidia อย่างทุกวันนี้หรอก

    • โปรเจ็กต์นี้ตั้งอยู่บนความพยายามสำคัญของ implementation WebGPU อย่าง wgpu-rs
      WebGPU ไม่ได้เหมาะที่สุดสำหรับแอปเนทีฟ
      มันถูกออกแบบบนพื้นฐานของ Vulkan เวอร์ชันเก่า (โดยเฉพาะก่อน RTX) และหลังจากนั้น native API ที่จริงจังก็พัฒนาไปไกลกว่ามาก

  • ตอนนี้มันยังหยาบอยู่ แต่แค่การที่สิ่งนี้เป็นไปได้ก็น่าเหลือเชื่อจริง ๆ
    ถ้าการพัฒนาแบบนี้เดินหน้าต่อไป ฉันรู้สึกว่ามันมีศักยภาพจะทำลายปัญหา vendor lock-in ที่รุนแรงในซอฟต์แวร์ GPU และเปิดทางให้เกิดการแข่งขันจริงระหว่างผู้ผลิตฮาร์ดแวร์ได้
    สักวันหนึ่งอาจมีโลกที่เราเขียนโมเดลแมชชีนเลิร์นนิงด้วย Rust แล้วรันได้ทั้งบน Nvidia และ AMD
    แน่นอนว่าถ้าจะเอาประสิทธิภาพสูงสุดก็คงต้องมีโค้ดที่จูนเฉพาะแต่ละผู้ขาย แต่นั่นเป็นปัญหาเรื่องการ optimize
    ถึงอย่างนั้นก็ยังมี kernel ที่พกพาได้ดีและรันข้ามแพลตฟอร์มได้

    • มีเฟรมเวิร์กแมชชีนเลิร์นนิง Rust ชื่อ https://burn.dev ที่มี backend หลายแบบ เช่น CUDA และ ROCm
      น่าจะลองดูได้

    • อนาคตที่เราเขียนโมเดลแมชชีนเลิร์นนิงด้วย Rust แล้วรันได้ทั้ง Nvidia และ AMD น่าจะยังยากภายในสิบปีนี้
      ในโลกความเป็นจริง ecosystem ทั้งหมดอย่าง jax กับ torch ตั้งอยู่บน Python
      การทำให้นักพัฒนาในสายงานทั้งหมดเปลี่ยนไปใช้เครื่องมือ Rust นั้นแทบจินตนาการไม่ออกเลย

  • ถ้านับชั้น abstraction ดู

    1. โค้ด Rust แบบ domain-specific
    2. abstraction ของ backend บน crate อย่าง cust, ash, wgpu
    3. abstraction ของแพลตฟอร์ม, ไดรเวอร์, API ใน wgpu เป็นต้น
    4. abstraction ของไดรเวอร์และแพลตฟอร์มใน Vulkan, OpenGL, DX12, Metal
    5. abstraction ของฮาร์ดแวร์เฉพาะผู้ขายในไดรเวอร์ (และน่าจะมีมากกว่านี้อยู่ข้างใน)
    6. ฮาร์ดแวร์
      เท่ากับว่ามีความซับซ้อนที่ซ่อนอยู่ อย่างน้อย 6 ชั้น
      ฉันเลยสงสัยว่าจะทะลุผ่านชั้นเหล่านี้ทั้งหมด แล้วสะท้อนจุดเฉพาะของแต่ละแพลตฟอร์มออกมาเป็นประสิทธิภาพได้จริงหรือไม่
    • สิ่งที่ rust-gpu ทำก็คือคอมไพล์ไปเป็น SPIRV (หรือก็คือ IR ของ Vulkan) ในท้ายที่สุด
      เพราะฉะนั้นชั้นที่ 2 และ 3 จึงอาจข้ามไปได้หรือวางเป็นโครงสร้างขนานก็ได้
      และยังใช้เครื่องมือใน ecosystem ของ Rust อย่าง cargo, cargo test, cargo clippy, rust-analyzer กับการพัฒนา GPU shader ได้เหมือนเดิม
      จริง ๆ แล้วฉันคิดว่าที่ GPU programming ยาก ไม่ใช่เพราะสถาปัตยกรรม GPU มันต่างดาวเกินไป แต่เป็นเพราะ ecosystem ทั้งหมดผูกติดกับผู้ขายและถูกฉุดไว้ด้วยเครื่องมือเก่า ๆ มากกว่า

    • เดโมนี้แน่นอนว่าคล้ายเครื่อง Rube Goldberg ที่ซับซ้อน แต่ก็เพราะนี่เป็นช่วงแรกที่สิ่งแบบนี้เพิ่งเป็นไปได้
      เมื่อเวลาผ่านไปมันจะกลายเป็นสิ่งที่เป็นธรรมชาติและบูรณาการมากขึ้น
      และข้อดีอีกอย่างของ ecosystem Rust คือคุณจะพัฒนาแบบ abstract หรือแบบเจาะจงได้เท่าที่ต้องการ
      ตัวอย่างเช่น ใช้ฟีเจอร์เฉพาะแพลตฟอร์มผ่าน std::arch หรือจะเขียนแอสเซมบลีก็ยังได้
      เปลี่ยน allocator หรือ panic handler ก็ได้ และเมื่อฟีเจอร์ externally implemented items ที่จะมาในอนาคตเปิดใช้ได้ ความยืดหยุ่นในการจัดการชั้น abstraction ก็จะยิ่งสูงขึ้นอีก

    • เป็นข้อสังเกตที่ดี
      แต่ชั้นที่ 4-6 นั้นก็มีอยู่เสมอแม้จะเป็น shader หรือโค้ด CUDA ก็ตาม
      ส่วนชั้นที่ 1 กับ 3 ก็จริง ๆ แล้วแค่ถูกแทนที่ด้วยชั้นอื่นเท่านั้น (โดยเฉพาะถ้าต้องข้ามแพลตฟอร์ม)
      ต่อให้โปรเจ็กต์ Rust นี้เพิ่ม abstraction เข้ามา ก็เพิ่มแค่ประมาณหนึ่งชั้น
      และในฐานะคนที่ทำงานกับชั้น 4-6 โดยตรง ขอยืนยันได้เลยว่าข้างในนั้นมีความซับซ้อนซ่อนอยู่อีกมหาศาล
      พูดตรง ๆ คือบางทีก็มีมากกว่านั้นอีก :P

    • ถ้ามองตามความเป็นจริง ผู้ใช้ส่วนใหญ่มักจะไปถึงแค่ชั้น (3) หรือ (4) สูงสุด
      มันไม่ได้เพิ่มขั้นตอนขนาดใหญ่อะไรนักในทางปฏิบัติ
      และสำหรับข้อมูลเพิ่มเติม ยังมีชั้น abstraction อยู่เหนือชั้น 6 ด้วย
      firmware กับ microarchitecture เป็นตัวที่ทำให้ชุดคำสั่งที่เรานึกถึงถูกนำไป implement จริง

    • ฉันคิดว่ามันก็ไม่ได้ต่างจากการดูแลคอมไพเลอร์และรันไทม์หลายแบบตามสถาปัตยกรรม CPU เท่าไร
      ก็มีทั้ง calling convention ต่างกัน เรื่อง endianness ต่างกัน และในระดับฮาร์ดแวร์ก็ยังมี firmware กับ microcode อีก

  • น่าทึ่งมากที่ crate แบบ no_std + no alloc เดิม ๆ สามารถไปรันบน GPU ได้แทบไม่ต้องแก้อะไรเลย
    มันให้ความรู้สึกว่าเปิดไอเดียการใช้งานใหม่ ๆ ได้มหาศาล

    • ถ้าเป็นโค้ดที่สมมติว่าจะรันบน CPU พอเอาไปใช้จริงในแง่ประสิทธิภาพก็คงออกมาต่างพอสมควร
  • สุดยอดจริง ๆ
    ตอนนี้รายชื่อโปรเจ็กต์ Rust GPU ก็เยอะมากแล้ว
    ตัวนี้ดูเป็น abstraction ระดับล่างกว่า burn และ burn ก็อยู่ต่ำกว่า candle อีกที
    ตอนนี้เหมือนสิ่งที่เหลือคือเพิ่ม backend อย่าง naga เข้าไปในโปรเจ็กต์ด้านบนเหล่านั้น
    ทุกคนเหมือนกำลังสร้างอะไรบางอย่างบนรากฐานที่ต่างกัน แต่คงเพราะงานของ naga เพิ่งเกิดขึ้นไม่นาน
    ขอเสริมด้วยว่า burn เน้นเรื่องการรองรับแพลตฟอร์ม
    แต่พอดูไปดูมา backend เดียวที่ใช้ naga คือ wgpu
    ถ้าอย่างนั้นสุดท้ายใช้แค่ wgpu ก็พอแล้วไม่ใช่หรือ?
    สรุปคือ wgpu/ash(vulkan, metal) ไม่ก็ cuda
    เกร็ดเพิ่มเติม: มีอีก crate หนึ่งที่ใกล้เคียงกับความพยายามนี้
    https://github.com/tracel-ai/cubecl
    [0]: https://github.com/tracel-ai/burn
    [1]: https://github.com/huggingface/candle/

  • ฉันยังสงสัยว่านี่นับว่าเป็น "Rust" ที่รันบน GPU จริง ๆ ไหม
    พอดูโค้ดผ่าน ๆ แล้ว โครงสร้างมันเหมือนเอาภาษา shader มาครอบบนไวยากรณ์ Rust ที่เต็มไปด้วย programming macro อีกที
    GPU programming ต่างจากทั่วไปมากจนฉันคิดว่าต้องระวังเป็นพิเศษ
    การใส่ abstraction แบบนี้อาจทำให้ optimization บางอย่างเป็นไปไม่ได้

    • ในทางปฏิบัติคือมันเป็นโครงสร้างที่เอาโค้ด Rust ที่รันได้จริงไปคอมไพล์เป็น spirv bytecode โดยตรง
  • ดีใจมากที่ได้เห็นโปรเจ็กต์นี้
    ฉันรู้สึกว่ามันช่วยนักพัฒนาที่ไม่อยากเข้าไปติดหล่มสงครามแพลตฟอร์มได้มาก
    ตัวอย่างอย่าง https://github.com/cogentcore/webgpu ก็ดี
    ฉันใช้ golang และขอแค่ให้ GPU ใช้งานได้ดีในทุกแพลตฟอร์มก็พอ แบบนี้จึงทำให้ใช้ GPU ได้ทุกที่
    ขอบคุณ Rust จริง ๆ