nbd-vram - เครื่องมือสำหรับใช้ VRAM ของ NVIDIA GPU เป็นพื้นที่สว็อปบน Linux
(github.com/c0dejedi)- nbd-vram เป็นเดมอนขนาดเล็กที่ช่วยให้สามารถใช้ VRAM ที่ว่างอยู่ของ NVIDIA GPU บน Linux เป็นพื้นที่สว็อปที่มีลำดับความสำคัญสูงได้
- เหมาะกับ โน้ตบุ๊กกราฟิกแบบไฮบริด ที่อัปเกรดหน่วยความจำได้ยากเพราะเป็นหน่วยความจำแบบบัดกรี และใช้ iGPU ของ AMD/ATI สำหรับแสดงผล โดยนำ VRAM ของ NVIDIA ที่ไม่ได้ใช้งานมาช่วยลดแรงกดดันด้านหน่วยความจำ
- สภาพแวดล้อมทดสอบคือ AMD/ATI + RTX 3070 Laptop, RAM 16GB, VRAM 8GB, NVIDIA driver 580.159.03, kernel 6.17, Pop!_OS และจัดสรร VRAM 7GB เป็นสว็อป รวมกับ zram และ SSD swap แล้วได้หน่วยความจำที่ระบุตำแหน่งได้รวมราว 46GB
- ลำดับการทำงานคือใช้ RAM ให้เต็มก่อน จากนั้น VRAM จะรับเพจที่ล้นผ่าน PCIe ต่อด้วย zram ที่บีบอัดด้วย CPU และสุดท้ายจึงค่อยใช้ SSD
- เดมอนจะจัดสรร VRAM ผ่าน CUDA driver API และให้บริการอุปกรณ์บล็อกผ่านโปรโตคอล NBD(Network Block Device) บน Unix socket โดยใช้ไดรเวอร์
nbdที่มีอยู่ในเคอร์เนลเพื่อเปิดเผยเป็น/dev/nbdXให้ใช้งานเหมือนอุปกรณ์สว็อปทั่วไป - เส้นทางข้อมูลคือ kernel swap subsystem →
/dev/nbdX→ nbd kernel driver → Unix socket → nbd-vram daemon →cuMemcpyHtoD/DtoH→ GPU VRAM - ไม่ต้องใช้เคอร์เนลโมดูลแยกหรือ NVIDIA kernel symbol จึงสามารถคงการทำงานไว้ได้โดยไม่ต้องรีบิลด์ใหม่หลังอัปเดตเคอร์เนลหรือไดรเวอร์
- วิธีที่ใช้ NVIDIA P2P API ล้มเหลวบน GeForce รุ่นคอนซูเมอร์ เพราะ
nvidia_p2p_get_pages_persistentคืนค่าEINVALและวิธีioremap_wcแบบเข้าถึง BAR1 โดยตรงก็ล้มเหลวเช่นกัน เนื่องจากการอ่านพื้นที่นอก display framebuffer ราว 16MiB จะได้ค่า 0 กลับมา - เส้นทางการคัดลอกผ่าน CUDA อย่าง
cuMemcpyHtoDและcuMemcpyDtoHสามารถทำงานกับ CUDA GPU ได้โดยไม่ต้องใช้สิทธิ์พิเศษ จึงทำให้การเข้าถึงผ่าน NBD เลี่ยงข้อจำกัดของ P2P และ BAR1 ได้ - ความต้องการของระบบคือ NVIDIA GPU ที่รองรับ CUDA, NVIDIA driver ที่มี
libcuda.so.1, โมดูล nbd ของ Linux kernel 3.0+,nbd-client,gcc,makeและไม่จำเป็นต้องมี CUDA toolkit - หลังติดตั้งแล้ว systemd service
vram-swap-nbdจะเริ่มทำงานอัตโนมัติเมื่อบูต และสามารถปรับเพดาน VRAM ที่จะใช้กับลำดับความสำคัญของสว็อปได้ผ่านVRAM_SETUP_SIZE_MBและVRAM_SWAP_PRIORITYใน/etc/systemd/system/vram-swap-nbd.service - เดมอนจะลองใช้ขนาด VRAM ที่ร้องขอก่อน และหากหน่วยความจำ GPU ไม่พอ จะลดลงครั้งละ 512MiB เพื่อจัดสรรใหม่ ดังนั้น
VRAM_SETUP_SIZE_MBจึงทำงานเป็นเพดาน ไม่ใช่ค่าขนาดบังคับ - หากเปิดการจัดการตามสถานะพลังงาน บริการจะหยุดอัตโนมัติเมื่อถอดไฟ AC หรือเมื่อแบตเตอรี่ต่ำกว่าค่ากำหนด และจะเริ่มใหม่เมื่อไฟกลับมา โดยคำสั่ง
systemctl stopที่สั่งเองจะไม่ถูกเขียนทับ - ในเบนช์มาร์กของ RTX 3070 Laptop นั้น NVMe เร็วกว่าในด้าน sequential throughput และ random I/O แบบต่อเนื่อง แต่ในด้าน latency ของการอ่าน 4K ที่ 1 request/sec นั้น VRAM มีค่าเฉลี่ย 335us ซึ่งเร็วกว่า NVMe ที่ 9.05ms ถึง 27 เท่า
- เผยแพร่ภายใต้ MIT License และในรีโพซิทอรียังมี
test-nbd.shสำหรับ smoke test,test-fill.shสำหรับตรวจสอบทั้งพาร์ทิชัน และสคริปต์เบนช์มาร์กสำหรับ throughput, IOPS และ latency มาด้วย
1 ความคิดเห็น
ความเห็นจาก Hacker News
ถ้าจัดการผ่าน CUDA ให้เหมือนเป็นที่เก็บไฟล์หรือเมานต์แบบหนึ่ง โอเวอร์เฮดจะสูง ดังนั้นถ้าใช้ BAR ก็น่าจะช่วยเพิ่มทั้ง throughput และ IOPS ได้ชัดเจน
ถ้าคำอธิบายคือมันเหมาะกับโน้ตบุ๊กที่ใช้หน่วยความจำแบบบัดกรีติดบอร์ดและไม่มีทางอัปเกรดได้ ก็ถือว่าตอบคำถามแรกที่ผุดขึ้นมาทันทีว่า ทำไมต้องสลับจาก RAM แพงไปยัง RAM ที่แพงยิ่งกว่า
แม้กรณีใช้งานจะดูเฉพาะทาง แต่ในสถานการณ์ที่ สลับไป SSD การเอา VRAM ว่าง 8GB มาใช้ก็เป็นไอเดียที่ดีเวลาจำเป็น
เช่น ถ้าซื้อ GPU มาเพื่อเล่นเกม ตอนที่ไม่ได้เล่นเกม เดสก์ท็อปเรนเดอร์ก็ไม่ได้ต้องการ 16GB VRAM ดังนั้นก็น่าจะเอาไปใช้ทำอย่างอื่นได้
แต่ต้องตั้งอยู่บนสมมุติฐานว่าเวลาจะเริ่มเกม ระบบสามารถคืน VRAM ที่ใช้เป็นสว็อปอยู่ได้ ซึ่งไม่แน่ใจว่าในทางปฏิบัติทำได้หรือไม่
บน Amstrad PCW ที่พบได้บ่อยในสหราชอาณาจักรช่วงกลางยุค 80 ถึงกลางยุค 90 สามารถใช้ RAM ได้สูงสุด 512kB และแบ่งส่วนค่อนข้างใหญ่ไปทำเป็น RAM disk ได้
ตอนคอมไพล์ด้วย Turbo Pascal ก็เร็วขึ้นมากเหมือนกัน :-)
ไอเดียนั้นดี แต่ที่นี่ดูเหมือนจะมีอะไรผิดพลาดอย่างมาก
เขาบอกว่าบน RTX 3070 Laptop ได้ sequential throughput แค่ประมาณ 1.3 GB/s ทั้งที่ชิป RTX 3070 นี้เป็น PCIe 4.0 x16 ซึ่งควรได้ 64GB/s และ GDDR6 ขนาด 8GB เองก็มีแบนด์วิดท์ 448GB/s
การสลับไป NVMe น่าจะเร็วกว่าเป็นสองเท่า แต่ latency จะสูงกว่า
แถมยังรันเบนช์มาร์กโดยใช้ ZRAM ซึ่ง ZRAM จะบีบอัดเพจก่อนเขียนลงสว็อป ผมไม่รู้แน่ชัดว่าโอเวอร์เฮดด้านประสิทธิภาพมากแค่ไหน แต่มีโอกาสสูงว่าค่อนข้างมาก
อย่างแรกเลย มันต้องผ่านไดรเวอร์ nbd ที่ขึ้นชื่อว่าช้าเพราะเป็นโปรแกรมใน user space และยังใช้ bounce buffer ใน user space ก่อนส่งไปยัง GPU อีกด้วย ถ้าเคอร์เนลต้องสลับเพจ มันต้องคัดลอกไปยังบัฟเฟอร์ที่เปิดให้ user space เห็นก่อน จากนั้นโปรแกรม user space ต้องถูกปลุกขึ้นมาอีกครั้งเพื่อออกคำสั่ง CUDA แล้วคัดลอกเพจนั้นไปยังหน่วยความจำของอุปกรณ์
nbd เองก็ไม่ได้รองรับ queue depth สูงหรือการรวมการเข้าถึงที่อยู่ติดกันได้ดีนัก ถ้าเคอร์เนลออกคำสั่งสลับเพจ 4K จำนวนมากโดยไม่รวมกัน แค่จะให้ได้ 4 GB/s ก็ต้องมีการสลับบริบทระหว่างเคอร์เนลกับ user space อย่างน้อยหนึ่งล้านครั้งต่อวินาทีแล้ว ยังไม่ต้องพูดถึง 64 GB/s นี่แค่ดูเฉพาะส่วน NBD ยังไม่รวมความซับซ้อนของไดรเวอร์ NVIDIA
PCIe ขนส่งข้อมูลได้มากก็จริง แต่ถ้าจะเข้าใกล้แบนด์วิดท์เต็ม ๆ ต้องใช้ DMA engine ที่มีรายการเพจยาว ๆ ถ้าตั้งค่าการส่งข้อมูลทีละเพจ 4K บน PCIe ก็ไม่มีทางทำให้บัสอิ่มตัวได้เต็มที่
เส้นทางการสลับไป NVMe ถูกปรับแต่งมาอย่างดีมาก ตัว swapper สามารถส่งรายการเพจให้ไดรเวอร์ NVMe ได้โดยตรง และคอนโทรลเลอร์ก็ดึงข้อมูลจาก RAM ด้วย DMA ได้โดยตรง จึงไม่มีทั้งการคัดลอกฝั่ง CPU และการสลับบริบทเลย
ถ้าย้ายไปใช้ไดรเวอร์ ublk อาจหลีกเลี่ยง bounce buffer ใน user space ได้ และอาจปรับปรุงเพิ่มได้ด้วยการตั้งค่า CUDA copy แบบขนานผ่านหลาย write queue
แต่ RAM หรือ VRAM ไม่เสื่อมจากการใช้งานแบบนั้น
บนเครื่องพัฒนาของผมมี 32GB RAM และมี 32GB VRAM ที่แทบไม่ได้ใช้เลยเวลาที่ไม่ได้รันโมเดล AI ดังนั้นไอเดียนี้ก็ไม่ได้แย่นัก
สงสัยว่าเขาจัดการกับ backpressure อย่างไร ถ้ามีคำขอจัดสรร VRAM เข้ามาในขณะที่ VRAM กำลังถูกใช้เป็นพื้นที่สว็อปจะเกิดอะไรขึ้น?
บน X11 บัฟเฟอร์ถูกจัดสรรล่วงหน้าเลยไม่แย่มาก แต่บน Wayland การจัดสรรเป็นแบบไดนามิกกว่ามาก ถ้า VRAM หมด เดสก์ท็อปทั้งระบบอาจพังได้ง่าย
ผมเคยเจออาการชนแบบนี้หลายครั้งตอนสลับเครื่องด้วย Hyprland+llama-server+KVM เพราะคืน VRAM ไม่ได้
การสร้าง อุปกรณ์สว็อป ในระดับผู้ใช้เป็นหนึ่งในปัญหาคลาสสิกที่เหมือนจะแก้ไม่ได้มานานแล้ว
ถ้าดีมอนต้อง swap-in เพจหนึ่ง แต่การจะ swap-in เพจนั้นได้ต้อง swap-in เพจของดีมอนเองก่อน จะทำอย่างไร?
อย่างน้อยก็เคยมีการถกกันแบบนี้ว่าเป็นเหตุผลที่ไมโครเคอร์เนลไม่มีวันใช้งานได้จริง ที่นี่ผมไม่แน่ใจว่าแนวทางแก้คืออะไร
เคอร์เนล Linux เองก็กันไม่ให้ text page ของตัวเองถูก swap-out อยู่แล้ว ดังนั้นวิธีแก้มีอยู่แล้ว และผมไม่เห็นเหตุผลว่าทำไมจะใช้กับการออกแบบไมโครเคอร์เนลไม่ได้
ถ้าล็อกหน่วยความจำทั้งหมดของดีมอนไว้ ปัญหานี้ก็กลายเป็นเรื่องเล็กน้อย
จำได้ว่าเคยทำอะไรคล้าย ๆ กันด้วยไดรเวอร์ MTD/phram ของ Linux: https://wiki.archlinux.org/title/Swap_on_video_RAM
แต่ไม่แน่ใจว่าตอนนี้ยังเกี่ยวข้องอยู่ไหม เพราะไม่รู้ว่ามันโต้ตอบกับ DRM อย่างไร และจัดการเรื่องการจอง VRAM บางส่วนอย่างไร วิธีที่เสนอให้จำกัดผ่าน xorg.conf ตอนนี้อาจจะล้าสมัยมากแล้ว
หน้านั้นยังมี FUSE filesystem ที่ทำบน OpenCL ด้วย: https://github.com/Overv/vramfs
อันนี้อาจเข้ากันได้ดีกว่า
ชวนให้คิดถึงวันเก่า ๆ
เคยเห็นอะไรคล้ายกันบน Windows เมื่อหลายปีก่อน
มันเป็นไดรเวอร์ proof of concept เชิงทดลองที่ให้สร้าง RAM drive จาก VRAM ของการ์ด NVIDIA ได้ โดยการเข้าถึงแบบ sequential เร็วอย่างที่คาด แต่ random access ยังมีช่องให้ปรับปรุงอีกมาก
GpuRamDrive สร้างไดรฟ์เสมือนที่รองรับด้วย GPU RAM: https://github.com/prsyahmi/GpuRamDrive
ฟอร์กที่รองรับ AMD: https://github.com/brzz/GpuRamDrive/
คล้ายกัน แต่ใช้ OpenCL API จึงทำงานบน AMD ได้ด้วย
เพียงแต่ไดรเวอร์ AMD มีบั๊กค่อนข้างเยอะ จนต้องนิยามก่อนว่าคำว่า “ทำงาน” หมายถึงอะไร: https://libguestfs.org/nbdkit-vram-plugin.1.html
ผมไม่เข้าใจว่าทำไม Apple Silicon Mac ที่มี RAM 32GB ยังเหลืออีก 20GB ที่ไม่ได้ใช้หรือเป็น “free” อยู่ ถึงยังใช้หรือถึงขั้นสร้าง swap file อยู่เลย
ทำไมถึงไม่มีคำสั่งง่าย ๆ แบบ
swapoff -aของ Linux สำหรับ ปิดการใช้งาน swap file ทั้งหมด ได้เลย?ถ้าไม่ได้ตั้งใจจะลดอายุ SSD โดยเฉพาะ มันก็ดูงี่เง่าไปหน่อย
ถ้ามีการตั้งค่าใน GUI สำหรับปิด swap file ก็คงดี และก็คงดีเหมือนกันถ้า Apple จะเลิกกับ “ขั้น” ของการจัดวาง system settings แบบปัจจุบันเสียที มันยังดูเหมือนสลัดคำพูดเมื่อเทียบกับแผง Preferences ตลอดหลายสิบปีที่ผ่านมา
#Apple #Feedback #swapfile
แนวคิดเรื่อง “หน่วยความจำที่ใช้งานได้” เอง ในอุดมคติก็ใกล้เคียงกับ “หน่วยความจำที่สามารถเรียกคืนได้อย่างรวดเร็วเพื่อนำไปใช้จุดประสงค์อื่น”
ในบางช่วงเวลา การปล่อยให้ข้อมูลไฟล์ที่แคชไว้เข้ามาใช้พื้นที่นั้น อาจดีกว่าการเก็บ anonymous memory ไว้ในหน่วยความจำหลักต่อไป