- เป็น เอนจิน inference สำหรับ LLM ที่พัฒนาด้วย C++/CUDA ซึ่งทำให้สามารถรัน โมเดล Llama 70B บน RTX 3090 (VRAM 24GB) ได้ผ่านการสตรีมหน่วยความจำ GPU และ direct I/O จาก NVMe
- ใช้ โครงสร้างแคชแบบปรับตัว 3 ชั้น เพื่อแบ่งการใช้งานระหว่าง VRAM, RAM แบบ pinned และ NVMe/mmap โดยอัตโนมัติ และทำความเร็วได้ สูงสุด 83 เท่าเมื่อเทียบกับ mmap
- แบ็กเอนด์ gpu-nvme-direct ส่งข้อมูลจาก NVMe ไปยัง GPU โดยตรงแบบข้าม CPU ทั้งหมด เพื่อใช้แบนด์วิดท์ PCIe ได้อย่างเต็มที่
- ฟีเจอร์ Layer skip และ self-speculative decoding ช่วยลดการคำนวณที่ไม่จำเป็นและเพิ่มความเร็วการประมวลผลโดยไม่สูญเสียคุณภาพ
- ช่วยให้สามารถรันโมเดลขนาดใหญ่มากได้อย่างมีประสิทธิภาพบนฮาร์ดแวร์สำหรับผู้บริโภค และชี้ให้เห็นถึงความเป็นไปได้ในการ ขยายการเข้าถึง inference ของ LLM ประสิทธิภาพสูง
ภาพรวมของ NTransformer
- เป็น เอนจิน inference LLM แบบ C++/CUDA ประสิทธิภาพสูง ที่รัน โมเดล Llama 70B บน RTX 3090 (VRAM 24GB)
- สตรีมเลเยอร์ของโมเดลผ่านหน่วยความจำ GPU และสามารถเลือกใช้ direct I/O จาก NVMe เพื่อข้าม CPU ทั้งหมดได้
- ไม่มี dependency ภายนอกนอกเหนือจาก CUDA Toolkit, ไม่ต้องใช้ PyTorch หรือ cuBLAS
- รองรับ ฟอร์แมตโมเดล GGUF และใช้รูปแบบ quantization ได้ทั้ง Q4_0, Q8_0, Q4_K_M, Q5_K, Q6_K, F16, F32
ประสิทธิภาพและโครงสร้างแคช
- แคชแบบปรับตัว 3 ชั้น (3-Tier Adaptive Caching)
- เลเยอร์ที่อยู่ใน VRAM (0 I/O)
- RAM แบบ pinned (สำหรับส่ง H2D เท่านั้น)
- fallback ไปยัง NVMe/mmap
- บนระบบ RTX 3090 + RAM 48GB ทำความเร็วได้ สูงกว่า mmap 83 เท่า
- แบนด์วิดท์ PCIe Gen3 x8 (ประมาณ 6.5 GB/s) เป็นคอขวดหลัก
- quantization แบบ Q4_K_M สามารถบรรจุเลเยอร์ใน VRAM ได้มากกว่าอีก 10 เลเยอร์ (36 เทียบกับ 26) จึงลดปริมาณการส่งข้อมูลลง
- Layer skip (อิงจาก cosine similarity) สามารถข้าม 20 เลเยอร์จากทั้งหมด 80 เลเยอร์ โดยสูญเสียคุณภาพน้อยมาก
ความสามารถหลัก
- การสตรีมแบบ SLEP: ซ้อนการอ่านจาก NVMe, PCIe DMA และการประมวลผลบน GPU ด้วย double buffering
- แบ็กเอนด์ gpu-nvme-direct: อ่านข้อมูลจาก NVMe เข้าสู่หน่วยความจำแบบ pinned ที่ GPU เข้าถึงได้โดยตรง
- Self-speculative decoding: ใช้เลเยอร์ที่อยู่ใน VRAM เป็น draft model โดยไม่ต้องมีโมเดลเพิ่มเติม
- การเลือกเส้นทางข้อมูลอัตโนมัติ: VRAM resident > pinned RAM H2D > mmap pinned > CPU memcpy
- รองรับสถาปัตยกรรม Llama: รวมถึง RoPE, GQA, SwiGLU, RMSNorm และ KV cache
ความต้องการของระบบ
- Linux (Ubuntu, kernel 6.17+), CUDA Toolkit 13.1, gcc/g++ 14, CMake 3.24+
- GPU ที่มี Compute Capability 8.0+ (ทดสอบบน RTX 3090)
- หากใช้ direct I/O จาก NVMe ต้องมี NVMe SSD บนสล็อต PCIe แยกต่างหาก และต้องใช้ไลบรารี gpu-nvme-direct
การสตรีมตรงจาก NVMe
- หากโมเดลมีขนาดใหญ่เกิน VRAM จะใช้ เส้นทางตรง NVMe → GPU โดยตัด CPU ออกทั้งหมด
- การไหลของข้อมูล: NVMe SSD → DMA → หน่วยความจำ staging แบบ pinned → PCIe H2D → บัฟเฟอร์ GPU → การคำนวณ
- bind NVMe เข้ากับ VFIO เพื่อให้เข้าถึงได้โดยตรงจาก user space
- แต่ละเลเยอร์ (ประมาณ 670MB สำหรับ 70B Q6_K) ถูกอ่านด้วยคำสั่ง NVMe 670 คำสั่งภายในเวลาราว 202ms
- การอ่าน NVMe, H2D DMA และการคำนวณบน GPU ถูกประมวลผลแบบขนานใน pipeline แบบ double buffer
การตั้งค่าระบบและคำเตือนด้านความเสี่ยง
- สคริปต์ตั้งค่าอัตโนมัติ (
setup_system.sh) จะคอนฟิก GRUB, NVIDIA DKMS, CUDA headers, VFIO, การ bind NVMe ตามลำดับ
- มีงานความเสี่ยงสูงรวมถึง การปิด IOMMU, การแพตช์ kernel module, การ bind NVMe เข้ากับ VFIO
- หากตั้งค่าผิดอาจทำให้ บูตไม่ขึ้น, ข้อมูลใน NVMe สูญหาย, ระบบไม่เสถียร
- ห้ามใช้ boot drive โดยเด็ดขาด, ต้องมีอุปกรณ์ NVMe แยกต่างหากสำหรับงานนี้
- มีสคริปต์สำหรับสำรองและกู้คืนการเปลี่ยนแปลงทั้งหมด
สถาปัตยกรรมและโครงสร้างโค้ด
- องค์ประกอบหลักในไดเรกทอรี
src/
core/: เทนเซอร์, การจัดสรรหน่วยความจำ, การจัดการอุปกรณ์ GPU
cuda/: เคอร์เนล GEMV, RMSNorm, RoPE, SwiGLU, softmax
memory/: เอนจินสตรีมมิง SLEP ที่ทำงานบน NVMe และ mmap
model/: องค์ประกอบ Transformer, ตัวโหลด GGUF, attention, FFN, normalization
inference/: tokenizer, sampler, engine
scripts/: รวมสคริปต์ตั้งค่าระบบ, การ bind NVMe และสคริปต์กู้คืน
โรดแมปการพัฒนา
- ระยะที่ 1: Llama 8B Q8_0, เคอร์เนล CUDA แบบคัสตอม, 48.9 tok/s (เสร็จสิ้น)
- ระยะที่ 2: การสตรีมแบบ SLEP, รัน 70B บน GPU เดี่ยว, เร็วขึ้น 33 เท่า (เสร็จสิ้น)
- ระยะที่ 3: รองรับ Q4_K_M/Q5_K, Layer skip, self-speculative decoding, F16 KV cache (เสร็จสิ้น)
- ระยะที่ 4: แบ็กเอนด์ NVMe Direct, การอ่าน NVMe ที่ขับเคลื่อนโดย GPU 3.35 GB/s (เสร็จสิ้น)
- ระยะที่ 5: ปรับแต่ง inference และเปิดเผย C API สาธารณะ (ตามแผน)
ไลเซนส์
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
คิดว่าวิธี ส่งข้อมูลจาก NVMe ไปยัง GPU โดยตรง โดยข้าม CPU นั้นฉลาดมาก
เวลารันโมเดลขนาดใหญ่บนเครื่องโลคัล คอขวดมักจะเป็นลำดับชั้นหน่วยความจำเสมอ แต่นี่เหมือนกับการใช้ NVMe เป็น VRAM แบบขยายผ่าน DMA โดยตรง
อยากรู้เหมือนกันว่าถ้าเทียบกับแนวทาง unified memory ของ Apple M series จะเป็นอย่างไร M4 Max สามารถโหลดโมเดล 70B เข้าเมโมรีได้ทั้งหมด แต่ throughput ต่ำกว่า 3090
ถ้ามีเบนช์มาร์กที่เทียบประสิทธิภาพแบบเนทีฟของ 3090 ที่ใช้แนวทาง NVMe กับ M4 Max โดยอิง batch inference ก็น่าจะน่าสนใจมาก
ถ้าใช้ GPUdirect ก็สามารถทำ DMA transfer ไปยังอุปกรณ์สตอเรจได้โดยตรง
เลยลองคิดดูว่าถ้า m.2 storage เป็น DRAM จริง ๆ จะเป็นอย่างไร ตอนที่ spill โมเดลจาก GPU จริง ๆ แล้วไม่ต้องการ persistence ดังนั้นอาจเก็บ system RAM ไว้ให้ CPU ใช้ได้
ความเร็ว 0.2 โทเคนต่อวินาทีนั้นช้าเกินไปสำหรับแชต แต่พอสำหรับ งานแบบแบตช์/อะซิงก์
ฉันรัน pipeline สร้างคอนเทนต์อัตโนมัติที่มีการเรียก LLM หลายตัวพร้อมกัน โดยคอขวดคือการสร้างภาพอยู่แล้ว ดังนั้นทั้งงานก็ใช้เวลาราว 20 นาทีอยู่ดี
ถ้ารันโมเดล 70B แบบโลคัลได้ ก็จะช่วยประหยัดค่า API token ได้มาก จึงเป็นการ ลดต้นทุน อย่างมีนัยสำคัญ
0.2 tok/s ถือว่าโอเคสำหรับการทดลอง แต่ไม่พอสำหรับการใช้งานแบบ interactive
ในกรณีส่วนใหญ่ โมเดล 8B หรือ 13B ที่ quantize มาดีแล้วจะให้ สมดุลระหว่าง latency กับคุณภาพ ได้ดีกว่า
เป็นการทดลองที่น่าสนใจมาก ฉันเองก็น่าจะลองอะไรแบบนี้ก่อน
อยากรู้ตัวเลข throughput จริงเมื่อเทียบกับแบนด์วิดท์เชิงทฤษฎีของ PCIe อยากรู้ว่ามันติดที่ latency หรือแบนด์วิดท์กันแน่
เป็นการแฮ็กที่เจ๋ง แต่ 0.5 tok/s สำหรับโมเดล 70B ก็ยังช้าเมื่อเทียบกับการที่การ์ดใบเดียวกันรันโมเดล 7B ได้ 30+ tok/s
ตามงานวิจัยของ NVIDIA โมเดลต่ำกว่า 10B ก็จัดการงานเอเจนต์ได้ 40~70% แล้ว และช่องว่างด้านคุณภาพก็กำลังลดลงอย่างรวดเร็ว
ด้านนี้ยังน่าทดลองต่ออีกมาก
ในระยะยาว สิ่งสำคัญน่าจะเป็นการทำ optimization ของโมเดล หรือก็คือ การค้นหาส่วนของโมเดลที่ตัดทิ้งได้โดยไม่กระทบประสิทธิภาพ เพราะท้ายที่สุดโมเดลก็เป็นโครงสร้างแบบ lossy compression อย่างหนึ่ง แนวทางนี้ยังช่วยเรื่อง การทำให้ AI เข้าถึงได้กว้างขึ้น ด้วย
เป็นโปรเจกต์ที่เจ๋งมาก อยากรู้ว่าต้องมีพื้นฐานความรู้ด้าน ระบบ/ฮาร์ดแวร์ แบบไหนถึงจะคิดไอเดียแบบนี้ออกมาได้
ฉันทำงานในสภาพแวดล้อมที่ฮาร์ดแวร์ถูก abstraction ไว้เยอะ เลยคิดแนวทางแบบนี้ได้ยาก ดูเหมือนต้องมีทั้งความคิดสร้างสรรค์และความเข้าใจระดับระบบจริง ๆ
ตอนพยายามรัน LLM บน PS2 ก็เจอข้อจำกัด RAM 32MB และ VRAM 4MB เลยคิดวิธี สตรีมเลเยอร์ ขึ้นมา PS2 สามารถให้ VRAM จัดการที่อยู่ 32 บิตได้โดยตรง เลยเร็วมาก และเขาพยายามจำลองสิ่งนั้นบนพีซีด้วย
ฉันก็กำลังลองอะไรคล้าย ๆ กันอยู่ กำลังทดลองรัน โมเดล 1T โดยใช้ VRAM ไม่ถึงครึ่ง
ดูเหมือนว่าจะปรับ routing layer ของ SGLang เพื่อทำ expert swap แบบอิง JIT prediction จาก Gen5 NVMe ไปยัง GPU memory ได้ โดยใช้ primitive ของ NVIDIA Dynamo และ NIXL
ไม่แน่ใจว่ามีใครลองทำมาก่อนหรือยัง
เป็นโปรเจกต์ที่เจ๋งมาก อยากรู้รายละเอียดเพิ่มเกี่ยวกับ ขั้นตอนแพตช์ DKMS บน GPU ทั่วไป ฉันก็อยากลองทำดู
ลิงก์ที่เกี่ยวข้อง: RTX4090 P2P Unlock, vGPU Unlock