Skip to main content

cufile-skill

NVIDIA cuFile / GPUDirect Storage (GDS) 高性能编程 — GPU 与 NVMe SSD 之间的直接 DMA 数据传输, 绕过 CPU 内存,实现最低延迟和最高吞吐。涵盖同步/异步/批量 I/O、缓冲区注册、 cufile.json 调优、GDS 兼容性检测与故障排查。 Use when writing, debugging, or tuning cuFile applications that need direct NVMe-to-GPU data transfer. Covers driver lifecycle, buffer/handle registration, sync/async/batch I/O, cufile.json configuration, performance tuning (alignment, IO size, batch aggregation), and GDS fallback detection. Triggers on: cuFile, cufile, GPUDirect Storage, GDS, GPU Direct Storage, GPU+NVMe, nvidia-fs, cuFileRead, cuFileWrite, cuFileBufRegister, cuFileHandleRegister, gdscheck, gdsio, gdscopyme, CUDA stream IO, GPU storage, GPU data pipeline, direct DMA, NVMe to GPU, GPU I/O acceleration, GPU直通存储, GPU数据流水线, 批量IO, 异步IO, O_DIRECT, cufile.json, libcufile.

Source facts

Repository
ForceInjection/cufile-skill
Last source activity
June 10, 2026 at 02:28
Detected SKILL.md language
English
Stars
4
Forks
2

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
cufile-skill
description
NVIDIA cuFile / GPUDirect Storage (GDS) 高性能编程 — GPU 与 NVMe SSD 之间的直接 DMA 数据传输, 绕过 CPU 内存,实现最低延迟和最高吞吐。涵盖同步/异步/批量 I/O、缓冲区注册、 cufile.json 调优、GDS 兼容性检测与故障排查。 Use when writing, debugging, or tuning cuFile applications that need direct NVMe-to-GPU data transfer. Covers driver lifecycle, buffer/handle registration, sync/async/batch I/O, cufile.json configuration, performance tuning (alignment, IO size, batch aggregation), and GDS fallback detection. Triggers on: cuFile, cufile, GPUDirect Storage, GDS, GPU Direct Storage, GPU+NVMe, nvidia-fs, cuFileRead, cuFileWrite, cuFileBufRegister, cuFileHandleRegister, gdscheck, gdsio, gdscopyme, CUDA stream IO, GPU storage, GPU data pipeline, direct DMA, NVMe to GPU, GPU I/O acceleration, GPU直通存储, GPU数据流水线, 批量IO, 异步IO, O_DIRECT, cufile.json, libcufile.
# cuFile / GPUDirect Storage 高性能编程 ## Core Philosophy **GDS is a DMA shortcut.** GPUDirect Storage enables the NVMe controller to DMA data directly into GPU memory via PCIe peer-to-peer (P2P) transfers. The CPU does not touch the data — no bounce buffer, no `cudaMemcpy`, no kernel-mode copies. **Compatibility mode is THE #1 pitfall.** cuFile silently falls back to a CPU bounce buffer (two PCIe hops: NVMe → CPU → GPU) when GDS prerequisites aren't met. Your code still works, but performance is 2–3× worse. Always verify with `cuFileDriverGetProperties()` and `gdscheck` before measuring performance. **Registration has cost. Reuse has value.** `cuFileBufRegister` and `cuFileHandleRegister` pin pages and set up DMA mappings — these are not free. Register once at init, reuse for the lifetime of the workload. Never register/deregister inside a hot loop. **The API is simple — the performance is in the details.** cuFile has only ~15 core functions. The hard part is alignment, IO sizing, stream ordering, batch aggregation, and knowing whether GDS is actually engaged. ## GPUDirect Storage: 30-Second Architecture ```text Traditional I/O (no GDS): NVMe SSD ──PCIe──► CPU DRAM (bounce buffer) ──PCIe──► GPU DRAM ─ Two PCIe hops, CPU memory bandwidth consumed, CPU involved GPUDirect Storage (GDS enabled): NVMe SSD ──PCIe P2P──► GPU DRAM (BAR-mapped) ─ Single PCIe hop, zero CPU memory, CPU only submits commands Compatibility mode (GDS unavailable, fallback): NVMe SSD ──PCIe──► CPU DRAM ──PCIe──► GPU DRAM ─ Looks the same to your code, but 2–3× worse throughput ``` **How it works under the hood:** 1. `cuFileBufRegister(devPtr, size)` → Pins GPU virtual address range, creates a BAR mapping visible to the PCIe root complex 2. `cuFileHandleRegister(fd, path)` → Verifies the file is on a GDS-compatible filesystem, creates a GDS file descriptor 3. `cuFileRead(file_handle, devPtr, size, offset, 0)` → Submits an NVMe read command with the GPU physical address as the data pointer. The NVMe controller DMAs directly to GPU memory. 4. NVMe completion interrupt → cuFile library processes completion → GPU buffer has the data **Key requirement:** The NVMe SSD and GPU must be on the same PCIe root complex with PCIe Access Control Services (ACS) disabled for P2P. `gdscheck` validates this topology. ## cuFile API Lifecycle: The 5-Step Pattern Every cuFile application follows this exact sequence. Get the order wrong = runtime errors. ```text Step 1: cuFileDriverOpen() └── Initialize GDS driver, negotiate API version Step 2: Allocate & Register GPU Buffers ├── cudaMalloc(&devPtr, size) └── cuFileBufRegister(devPtr, size, 0) Step 3: Open & Register File ├── fd = open(path, O_DIRECT | O_RDONLY) └── cuFileHandleRegister(&file_handle, &descr) Step 4: Perform I/O ├── cuFileRead(file_handle, devPtr, size, offset, 0) ├── cuFileWrite(file_handle, devPtr, size, offset, 0) ├── cuFileReadAsync(file_handle, devPtr, size, offset, stream) └── cuFileWriteAsync(file_handle, devPtr, size, offset, stream) Step 5: Cleanup (reverse order of setup) ├── cuFileHandleDeregister(file_handle) ├── close(fd) ├── cuFileBufDeregister(devPtr) ├── cudaFree(devPtr) └── cuFileDriverClose() ``` **Critical ordering rule:** Cleanup in strict reverse order. Deregister the handle BEFORE closing the file descriptor. Deregister the buffer BEFORE freeing GPU memory. Otherwise you get `CU_FILE_INVALID_HANDLE` or worse — silent memory corruption if the GPU frees a page the DMA engine still references. ## API Quick Reference ### Driver & Lifecycle | Function | Purpose | Perf Impact | | ----------------------------------- | ----------------------------------------- | --------------- | | `cuFileDriverOpen()` | Initialize cuFile driver, negotiate API | One-time cost | | `cuFileDriverClose()` | Shutdown cuFile driver | Cleanup only | | `cuFileDriverGetProperties()` | Query driver version and GDS capabilities | Diagnostic tool | | `cuFileIsGpuDirectStorageCapable()` | Check if platform can support GDS | Diagnostic tool | | `cuFileIsGpuDirectStorageEnabled()` | Check if GDS is currently engaged | Diagnostic tool | ### Buffer Management | Function | Purpose | Perf Impact | | -------------------------- | ------------------------------------------ | ------------------------------- | | `cuFileBufRegister()` | Register GPU buffer for DMA | **Critical** — enables GDS path | | `cuFileBufDeregister()` | Unregister GPU buffer | Cleanup only | | `cuFileBufSetAllocFlags()` | Set allocation flags for registered buffer | Advanced tuning | ### File Handle Management | Function | Purpose | Perf Impact | | -------------------------- | ------------------------------------ | ------------------------------- | | `cuFileHandleRegister()` | Register file descriptor for GDS I/O | **Critical** — enables GDS path | | `cuFileHandleDeregister()` | Unregister file handle | Cleanup only | ### Synchronous I/O | Function | Purpose | Perf Impact | | --------------- | ------------------------------ | --------------------- | | `cuFileRead()` | Read from file into GPU buffer | Blocks calling thread | | `cuFileWrite()` | Write GPU buffer to file | Blocks calling thread | ### Asynchronous I/O (CUDA Streams) | Function | Purpose | Perf Impact | | ------------------------ | ----------------------------------------- | ------------------------------- | | `cuFileReadAsync()` | Read into GPU buffer on a CUDA stream | Overlaps I/O with compute | | `cuFileWriteAsync()` | Write GPU buffer to file on a CUDA stream | Overlaps I/O with compute | | `cuFileStreamRegister()` | Associate a CUDA stream with cuFile | Required before first async I/O | ### Batch I/O | Function | Purpose | Perf Impact | | -------------------------- | ------------------------------------ | ------------------------------------- | | `cuFileBatchIOSetUp()` | Create batch I/O handle | Setup cost, amortized across requests | | `cuFileBatchIOSubmit()` | Submit multiple I/O requests at once | **Critical** — 2-10× throughput gain | | `cuFileBatchIOGetStatus()` | Check completion status of batch | Polling overhead | | `cuFileBatchIOCancel()` | Cancel pending batch requests | Error recovery | | `cuFileBatchIODestroy()` | Tear down batch I/O handle | Cleanup only | ### Error & Diagnostic Utilities | Function | Purpose | | ------------------------ | ----------------------------------------- | | `cuFileGetErrorString()` | Convert error code to human-readable text | | `cuFileOpError()` | Query last error for a specific operation | ## Performance Tuning: The Complete Checklist ### Before You Start: Verify GDS is Working **This is the single most important step.** Many "perf tuning" sessions end with "GDS was never engaged." ```bash # 1. Check GDS toolchain gdscheck -p # Platform check: GPU topology, PCIe P2P, ACS state gdscheck -f /mnt/nvme # Filesystem check: mount options, O_DIRECT support gdsio -f /mnt/nvme/test # Functional test: write + read + verify # 2. Verify in code CUfileDrvProps_t props; cuFileDriverGetProperties(&props); printf("GDS capable: %s\n", props.is_gds_capable ? "YES" : "NO"); printf("GDS enabled: %s\n", props.is_gds_enabled ? "YES" : "NO"); printf("Version: %d.%d\n", props.major, props.minor); ``` ### Performance Tuning Checklist (ordered by impact) - [ ] **Buffer Alignment** — GPU buffers must be 4KB-aligned for GDS. Use `cuMemAlloc` (not `cudaMalloc`) for guaranteed alignment. Misaligned buffers silently fall back to compat mode. - [ ] **IO Size** — Minimum 4KB for GDS path. For high throughput, use IO sizes ≥ 1MB. Small IOs (≤ 64KB) have high per-operation overhead. Batch them. - [ ] **File Open Flags** — Files MUST be opened with `O_DIRECT` for the GDS path. Without it, you get compat mode. Also use `O_RDONLY` or `O_WRONLY` — O_RDWR has additional locking overhead. - [ ] **Buffer Registration Strategy** — Register buffers once at initialization, reuse for thousands of IOs. Registration pins GPU pages — pinning is expensive. Unregistration unpins. A hot loop that registers/unregisters is wasting 90%+ of its time in pinning. - [ ] **Batch I/O** — For workloads with many small IOs, use `cuFileBatchIOSetUp` + `cuFileBatchIOSubmit`. Reduces per-IO submission overhead by 2–10×. See `references/batch-io.md`. - [ ] **Async I/O + CUDA Stream Overlap** — Use `cuFileReadAsync` on one stream while your kernel runs on another. This hides I/O latency behind compute. See `references/async-io.md`. - [ ] **CUDA Stream Registration** — Call `cuFileStreamRegister()` once per stream before first async I/O. Don't re-register the same stream. - [ ] **Thread to File Mapping** — For multi-file workloads, dedicate one CPU thread per file. cuFile handles are not thread-safe for concurrent operations on the same handle. - [ ] **NUMA Affinity** — CPU threads submitting I/O should be on the NUMA node closest to the PCIe root complex serving the NVMe device. Use `numactl --cpunodebind=N` or `sched_setaffinity()`. - [ ] **cufile.json Tuning** — The global configuration file at `/etc/cufile.json` (or `$CUFILE_CONFIG`) controls GDS behavior. Key parameters: - `max_direct_io_size`: Maximum IO size for direct GDS path (default: 1GB) - `max_device_cache_size`: Maximum device-internal cache for GDS (default: 128MB) - `max_device_pinned_mem_size`: Maximum device pinned memory (default: unlimited) - `enable_compat_mode`: Allow fallback to compat mode (default: true — turn off to get hard errors instead of silent fallback) - [ ] **Filesystem Choice** — GDS requires: ext4, xfs, or GPFS with specific mount options. NFS/CIFS/FUSE are NOT supported. Verify with `gdscheck -f <mountpoint>`. - [ ] **PCIe Topology** — GPU and NVMe must be on the same PCIe root complex with ACS disabled for P2P. Multi-socket systems often have separate root complexes — if GPU is on socket 0 and NVMe on socket 1, GDS may not work. ### IO Size Guidelines | Workload | Recommended IO Size | Batch? | Reasoning | | ----------------------------- | ------------------- | ---------------- | ---------------------------------------------- | | Small random reads (4KB-64KB) | 4KB-64KB | **Yes** | Batch amortizes per-IO cost 2–5× | | Medium sequential reads | 256KB-1MB | Optional | Large enough that per-IO cost is low | | Large sequential reads | 1MB-16MB | No | Single IO saturates PCIe bandwidth | | Checkpoint / model save | 16MB-256MB | No | Max throughput via single large write | | Data pipeline (train+ingest) | 1MB-8MB | Yes (double buf) | Batch N reads while kernel processes batch N-1 | ## Configuration: cufile.json Reference The cuFile configuration file controls global GDS behavior. Default location: `/etc/cufile.json`. Override with environment variable `CUFILE_CONFIG`. ### Complete Key Reference ```json { "properties": { "max_device_cache_size": 134217728, "max_device_pinned_mem_size": 0, "max_direct_io_size": 1073741824, "max_device_allocated_mem_size": 0, "enable_compat_mode": true, "profiling": { "enable": false, "trace": false }, "fs": { "ext4": { "max_direct_io_chunk_size": 0 }, "xfs": { "max_direct_io_chunk_size": 0 } } } } ``` | Key | Default | Description | Tuning Guidance |
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub