ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

KvikIO GPU 文件 I/O 实战指南:用 GPUDirect Storage 打通存储与显存

KvikIO GPU 文件 I/O 实战指南:用 GPUDirect Storage 打通存储与显存 KvikIO GPU 文件 I/O 实战指南用 GPUDirect Storage 打通存储与显存【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000 scientists worldwide. 165 ready-to-use validated skills plus 100 scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills本文是 scientific-agent-skills 仓库中 optimize-for-gpu 技能下关于 KvikIO 的完整技术指南。KvikIO 是 RAPIDS 生态中的高性能文件 I/O 库通过封装 NVIDIA cuFile 提供 GPUDirect StorageGDS能力让数据在存储与显存之间直接流动、彻底绕开 CPU 内存中转当 GDS 不可用时又能无缝回退到 POSIX I/O。读完本文你将掌握如何安装与验证 KvikIO、用CuFile读写本地文件、用RemoteFile从 S3/HTTP/WebHDFS 直接读入 GPU、在 Zarr 上使用GDSStore、按需调优线程池与任务大小等运行时参数以及如何用非阻塞 I/O 实现数据加载与 GPU 计算的流水线重叠。什么是 KvikIO为 GPU 工作负载而生的文件 I/O 层KvikIO 是一个同时提供 Python 与 C 接口的高性能文件 I/O 库核心价值来自它对 NVIDIA cuFile 的绑定GPUDirect StorageGDS启用后读写操作在 NVMe 存储与 GPU 显存之间直接传输完全绕开 CPU 内存即不再经过磁盘 → 主机内存 → 显存的两次拷贝路径优雅回退当 GDS 不可用例如缺少libcufile.so、运行在 WSL、Docker 未挂载/run/udev时自动回退到 POSIX I/O同时照常支持主机内存与设备内存两类缓冲生态互操作KvikIO 隶属于 RAPIDS 生态与 CuPy、cuDF、Numba 及其他 GPU 库通过 CUDA Array Interface 零拷贝互通。在 decision_framework.md 的选型矩阵中KvikIO 被定位为原始/远程文件 I/O场景的首选路径当工作负载是把大二进制文件直接加载进显存把 GPU 数组直接写盘从 S3/HTTP/WebHDFS 读入显存在 GPU 上使用 Zarr 分块数组或文件 I/O 成为存储与 GPU 之间的瓶颈时使用 KvikIO。而 SKILL.md 同时强调对 CSV、Parquet、JSON 这类表格化格式应改用 cuDF 自带的高性能读取器KvikIO 只负责原始二进制数据与远程文件访问。安装与验证KvikIO 属于 RAPIDS 26.06 系列包发布为-cu12与-cu13两种 wheel 变体需要根据本机 CUDA 版本选择。仓库约定使用uv add且 RAPIDS 包需要 NVIDIA 的 extra index因为部分包的可用性差异许多包也同时镜像在 PyPI 上# CUDA 12.x uv add kvikio-cu1226.6.* # CUDA 13.x uv add kvikio-cu1326.6.* # 可选Zarr 支持KvikIO 26.06 的 GPU store 后端依赖 Zarr 3.x uv add zarr3.*对应 installation.md 中的完整写法是显式带上 NVIDIA 索引uv add --extra-index-urlhttps://pypi.nvidia.com kvikio-cu1226.6.* uv add zarr3.* # 可选用于 Zarr GPU 后端安装后验证 GDS 是否可用import kvikio import kvikio.cufile_driver # True 表示 GDS 已就绪False 表示将回退到 POSIX I/O print(kvikio.cufile_driver.get(is_gds_available))需要注意的运行前提SKILL.md 中的兼容性声明GPU 执行要求 NVIDIA CUDA 显卡RAPIDS 26.06 要求 Python 3.11 且运行于 Linux 或 WSL2并匹配 CUDA 12 或 13 的 wheel。什么时候该用以及不该用KvikIO适合使用 KvikIO 的场景把大型二进制数据直接加载到显存——省去标准open()或 NumPyfromfile()所必需的 CPU 内存拷贝把 GPU 数组直接写盘——从设备内存直接落盘无需先拷回主机从远程存储S3、HTTP、WebHDFS读入显存——跳过主机内存中转在 GPU 上处理 Zarr 数组——GDSStore后端直接把 chunk 读成 CuPy 数组I/O 成为瓶颈——GDS 可逼近裸 NVMe 带宽每块盘约 6–7 GB/s而标准 I/O 受限于 CPU 内存带宽需要 I/O 与计算重叠——非阻塞读写让你把数据加载与 GPU 计算流水化。KvikIO 不合适的场景数据很小 1 MB——内核启动与 GDS 开销占主导得不偿失读取结构化格式CSV、Parquet、JSON——应使用 cuDF 自带的高性能读取器cudf.read_csv()、cudf.read_parquet()等只需要主机内存——标准 Python I/O 更简单。CuFile本地文件 I/Okvikio.CuFile是本地文件 I/O 的主接口面向 GPU 工作负载时替代 Python 的open()。基本用法import cupy as cp import kvikio # 把 GPU 数组写入磁盘 a cp.arange(1_000_000, dtypecp.float32) with kvikio.CuFile(data.bin, w) as f: f.write(a) # 再读回来 b cp.empty(1_000_000, dtypecp.float32) with kvikio.CuFile(data.bin, r) as f: f.read(b) assert cp.all(a b)API 方法一览方法阻塞说明read(buf, size, file_offset)是读入设备或主机缓冲write(buf, size, file_offset)是从设备或主机缓冲写出pread(buf, size, file_offset)否非阻塞并行读返回IOFuturepwrite(buf, size, file_offset)否非阻塞并行写返回IOFutureraw_read(buf, size, file_offset)是底层单线程读仅设备raw_write(buf, size, file_offset)是底层单线程写仅设备raw_read_async(buf, stream, size, file_offset)否基于 CUDA 流的异步读仅设备raw_write_async(buf, stream, size, file_offset)否基于 CUDA 流的异步写仅设备文件模式与open()一致r读、w写/截断、a追加、读写。用 Future 实现非阻塞 I/Opread/pwrite会把操作拆分为多个任务交给线程池执行并返回IOFuture让你在 I/O 进行期间穿插其他工作import cupy as cp import kvikio data cp.empty(10_000_000, dtypecp.float32) with kvikio.CuFile(data.bin, r) as f: # 为不同区段发起两个非阻塞读 future1 f.pread(data[:5_000_000]) future2 f.pread(data[5_000_000:], file_offset5_000_000 * 4) # I/O 进行期间可以做其他事... # 等待完成 bytes_read1 future1.get() bytes_read2 future2.get()部分读取与写入import cupy as cp import kvikio # 只读文件的一部分 buf cp.empty(1000, dtypecp.float32) with kvikio.CuFile(data.bin, r) as f: # 从字节偏移 4000 处开始读 4000 字节1000 个 float32 f.read(buf, size4000, file_offset4000)主机内存支持KvikIO 对主机内存的读写是透明的无需任何特殊 APIimport numpy as np import kvikio # 从主机内存写出 a np.arange(1_000_000, dtypenp.float32) with kvikio.CuFile(data.bin, w) as f: f.write(a) # 读入主机内存 b np.empty_like(a) with kvikio.CuFile(data.bin, r) as f: f.read(b)GDS 对齐规则GDS 在页对齐 I/O 下表现最佳。GPU 页大小为 4 KiB4096 字节文件偏移应为 4096 的倍数传输大小应为 4096 的倍数。KvikIO 能正确处理未对齐的 I/O但会将其拆分为对齐与未对齐两部分分别处理因此对齐 I/O 会明显更快。从源码结构看见下文性能优化小节这也是性能调优的首要检查项。RemoteFileS3、HTTP 与 WebHDFSkvikio.RemoteFile可直接把远程文件读入 GPU 或主机内存。HTTP/HTTPSimport cupy as cp import kvikio buf cp.empty(1_000_000, dtypecp.float32) with kvikio.RemoteFile.open_http(https://example.com/data.bin) as f: print(f.nbytes()) # 文件大小 f.read(buf)AWS S3import cupy as cp import kvikio # 使用桶名 对象名需要 AWS 环境变量或显式凭据 with kvikio.RemoteFile.open_s3(my-bucket, data/file.bin) as f: buf cp.empty(f.nbytes(), dtypecp.uint8) f.read(buf) # 使用 S3 URL with kvikio.RemoteFile.open_s3_url(s3://my-bucket/data/file.bin) as f: buf cp.empty(f.nbytes(), dtypecp.uint8) f.read(buf) # 公有 S3无需凭据 with kvikio.RemoteFile.open_s3_public(s3://public-bucket/data.bin) as f: buf cp.empty(f.nbytes(), dtypecp.uint8) f.read(buf) # 预签名 URL with kvikio.RemoteFile.open_s3_presigned_url(presigned_url) as f: buf cp.empty(f.nbytes(), dtypecp.uint8) f.read(buf)AWS 凭据来自环境变量AWS_DEFAULT_REGION、AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY也可作为关键字参数显式传入。自动识别端点类型import kvikio # KvikIO 会根据 URL 自动识别协议 with kvikio.RemoteFile.open(s3://bucket/object) as f: ... with kvikio.RemoteFile.open(https://example.com/file.bin) as f: ...WebHDFSimport kvikio with kvikio.RemoteFile.open_webhdfs(http://namenode:9870/path/to/file) as f: buf cp.empty(f.nbytes(), dtypecp.uint8) f.read(buf)RemoteFile 读入主机内存RemoteFile 同样可以轻松读入主机内存import numpy as np import kvikio with kvikio.RemoteFile.open_http(https://example.com/data.bin) as f: buf np.empty(f.nbytes(), dtypenp.uint8) f.read(buf)Zarr 集成GDSStoreKvikIO 为 Zarr3.x提供了 GPU store 后端GDSStore可以把分块的 N 维数组通过 GDS 直接读写到 GPU 显存import zarr from kvikio.zarr import GDSStore # 在 Zarr 中启用 GPU 支持 zarr.config.enable_gpu() # 创建 GDS 后端 store store GDSStore(rootdata.zarr) # 创建并写入 Zarr 数组数据始终留在 GPU 上 z zarr.create_array( storestore, shape(1000, 1000), chunks(100, 100), dtypefloat32, overwriteTrue, ) # 读取返回 CuPy 数组 chunk z[:100, :100] # 返回 cupy.ndarrayZarr KvikIO 的典型应用场景气候/气象数据大型多维数组生物信息学基因组数组任何需要 GPU 处理的分块数组工作负载。依赖要求在 KvikIO 26.06 基础上额外安装zarr3.*见安装小节。在 decision_framework.md 的组合建议中KvikIO Zarr是明确推荐的组合之一用 GDSStore 后端在 GPU 上直接读写分块 N 维数组。内存映射文件kvikio.mmap.Mmap提供内存映射文件访问同时支持主机与设备两种目的地from kvikio.mmap import Mmap import cupy as cp # 映射文件用于读取 with Mmap(data.bin, flagsr) as m: print(m.file_size()) # 顺序读入设备内存 buf cp.empty(1000, dtypecp.float32) m.read(buf, size4000, offset0) # 并行读返回 IOFuture future m.pread(buf, size4000, offset0) future.get()运行时设置KvikIO 的行为通过环境变量或kvikio.defaultsAPI 控制。关键设置项设置项环境变量默认值说明兼容模式KVIKIO_COMPAT_MODEAUTOON仅 POSIXOFF仅 GDSAUTO先试 GDS失败回退线程池大小KVIKIO_NTHREADS1pread/pwrite使用的 I/O 线程数任务大小KVIKIO_TASK_SIZE4 MiB单个并行 I/O 任务的最大字节数GDS 阈值KVIKIO_GDS_THRESHOLD16 KiB使用 GDS 的最小字节数更小则走 POSIX反弹缓冲大小KVIKIO_BOUNCE_BUFFER_SIZE16 MiB每个线程的中间主机缓冲大小直接 I/O 读KVIKIO_AUTO_DIRECT_IO_READoff读操作的机会性O_DIRECT直接 I/O 写KVIKIO_AUTO_DIRECT_IO_WRITEon写操作的机会性O_DIRECT编程式配置import kvikio.defaults # 查询设置 print(kvikio.defaults.get(compat_mode)) print(kvikio.defaults.get(num_threads)) # 运行时修改设置 kvikio.defaults.set({num_threads: 16, task_size: 8 * 1024 * 1024}) # 启用读操作的直接 I/O kvikio.defaults.set({auto_direct_io_read: True})兼容模式详解当 GDS 不可用缺少libcufile.so、运行于 WSL、Docker 未挂载/run/udev时AUTO模式会自动回退到 POSIX I/O。这意味着用 KvikIO 写的代码在任何地方都能跑——GDS 可用时只是跑得更快。import kvikio.cufile_driver # 确认 GDS 是否真正生效 print(kvikio.cufile_driver.get(is_gds_available))cuFile 驱动配置import kvikio.cufile_driver # 查询驱动属性 print(kvikio.cufile_driver.get(is_gds_available)) print(kvikio.cufile_driver.get(major_version)) # 配置可设置属性 kvikio.cufile_driver.set(max_device_cache_size, 1024) # 作为上下文管理器使用退出时自动还原 with kvikio.cufile_driver.set({poll_mode: True}): # 此处 poll mode 生效 ... # 退出后 poll mode 自动还原性能优化结合 SKILL.md 的优化工作流先建立基线与契约、确认 GPU 适配性、优先最小改动方案、验证语义、正确计时KvikIO 自身的调优点如下。1. 提高线程池大小默认 1 个线程偏保守。对大型文件应调大import kvikio.defaults kvikio.defaults.set({num_threads: 16})pread/pwrite会把单次操作拆分成多个任务交给该线程池执行因此线程数直接影响大文件的并行吞吐。同时可配合KVIKIO_TASK_SIZE控制每个任务的粒度。2. 用非阻塞 I/O 实现流水线使用pread/pwrite让 I/O 与计算重叠import cupy as cp import kvikio # 流水线读第 N 个 chunk 时处理第 N-1 个 chunk chunk_size 10_000_000 buf_a cp.empty(chunk_size, dtypecp.float32) buf_b cp.empty(chunk_size, dtypecp.float32) with kvikio.CuFile(large_data.bin, r) as f: # 发起第一次读 future f.pread(buf_a) future.get() for offset in range(chunk_size * 4, file_size, chunk_size * 4): # 处理当前块的同时发起下一次读 next_future f.pread(buf_b, file_offsetoffset) # 在 GPU 上处理 buf_a与 I/O 重叠 result cp.fft.fft(buf_a) next_future.get() buf_a, buf_b buf_b, buf_a # 交换缓冲3. 对齐到页边界GDS 在 4 KiB 对齐的偏移与大小下性能最佳# 好偏移与大小均对齐 f.read(buf, size4096 * 1000, file_offset4096 * 10) # 较慢未对齐KvikIO 能处理但会拆成对齐 未对齐两部分 f.read(buf, size5000, file_offset100)4. 启用直接 I/O对顺序写与冷读直接 I/O绕过操作系统页缓存可能有帮助import kvikio.defaults kvikio.defaults.set({ auto_direct_io_read: True, auto_direct_io_write: True, })注意默认值是读关、写开见运行时设置表开启前请确认文件系统与文件访问模式兼容O_DIRECT的对齐要求。5. 调节任务大小与反弹缓冲对非常大的文件增大任务与反弹缓冲大小import kvikio.defaults kvikio.defaults.set({ task_size: 16 * 1024 * 1024, # 每个任务 16 MiB bounce_buffer_size: 64 * 1024 * 1024, # 反弹缓冲 64 MiB })反弹缓冲是 GDS 路径上每个线程的中间主机缓冲当 I/O 无法对齐或需要兼容处理时数据经由它中转因此其大小会影响大块传输的吞吐上限。6. 页缓存工具基准测试时清空页缓存可以测量冷读性能import kvikio # 检查页缓存驻留情况 pages_cached, total_pages kvikio.get_page_cache_info(data.bin) print(f{pages_cached}/{total_pages} pages in cache) # 丢弃单个文件的页缓存无需提权26.04 起提供 kvikio.drop_file_page_cache(data.bin) # 丢弃系统级页缓存需要提升权限 kvikio.drop_system_page_cache()kvikio.clear_page_cache()自 26.04 起已弃用——请改用drop_system_page_cache()或按文件的drop_file_page_cache()。与其他库的互操作KvikIO 支持任何实现了 CUDA Array Interface 的缓冲因此与整个 RAPIDS 生态零拷贝互通。与 CuPy 配合读入 CuPy 数组是最常见用法import cupy as cp import kvikio data cp.empty(1_000_000, dtypecp.float64) with kvikio.CuFile(data.bin, r) as f: f.read(data) # data 现在是一个 CuPy 数组可直接用于 GPU 计算与 Numba CUDA 配合from numba import cuda import kvikio d_arr cuda.device_array(1_000_000, dtypefloat32) with kvikio.CuFile(data.bin, r) as f: f.read(d_arr)与 cuDF 配合对非表格化的原始二进制数据先用 KvikIO 加载再转换为 cuDFimport cupy as cp import cudf import kvikio # 加载原始 float 数组包装为 cuDF Series buf cp.empty(1_000_000, dtypecp.float32) with kvikio.CuFile(signal.bin, r) as f: f.read(buf) signal cudf.Series(buf)对于表格化格式CSV、Parquet、JSON、ORC请使用 cuDF 自带的读取器——它们针对这些格式做了专门优化这与 decision_framework.md 中的明确提示一致。与 NumPy 配合主机内存import numpy as np import kvikio arr np.empty(1_000_000, dtypenp.float32) with kvikio.CuFile(data.bin, r) as f: f.read(arr)在 decision_framework.md 的组合矩阵中KvikIO CuPy经 GDS 绕开 CPU 内存把原始二进制直接读入 CuPy 数组与KvikIO Numba读入 GPU 后用自定义 Numba CUDA 内核处理是官方推荐的组合模式。常见模式保存与加载 GPU 模型检查点import cupy as cp import kvikio def save_checkpoint(arrays: dict[str, cp.ndarray], path: str): 把多个 GPU 数组保存到单个文件。 with kvikio.CuFile(path, w) as f: offset 0 for arr in arrays.values(): f.write(arr, file_offsetoffset) offset arr.nbytes def load_checkpoint(shapes_dtypes: dict, path: str) - dict[str, cp.ndarray]: 从检查点文件加载 GPU 数组。 arrays {} with kvikio.CuFile(path, r) as f: offset 0 for name, (shape, dtype) in shapes_dtypes.items(): arr cp.empty(shape, dtypedtype) f.read(arr, file_offsetoffset) offset arr.nbytes arrays[name] arr return arrays从 S3 流式读入 GPU 并处理import cupy as cp import kvikio with kvikio.RemoteFile.open_s3(my-bucket, large-dataset.bin) as f: total_bytes f.nbytes() chunk_size 100 * 1024 * 1024 # 100 MB 块 buf cp.empty(chunk_size // 4, dtypecp.float32) for offset in range(0, total_bytes, chunk_size): size min(chunk_size, total_bytes - offset) f.read(buf[:size // 4], sizesize, file_offsetoffset) # 在 GPU 上处理当前块 result cp.mean(buf[:size // 4])用 KvikIO 替换 GPU 工作负载中的open()# 之前CPU 侧文件 I/O两次拷贝 import numpy as np data np.fromfile(data.bin, dtypenp.float32) import cupy as cp gpu_data cp.asarray(data) # 额外拷贝磁盘 → CPU → GPU # 之后直接到 GPU import cupy as cp import kvikio gpu_data cp.empty(1_000_000, dtypecp.float32) with kvikio.CuFile(data.bin, r) as f: f.read(gpu_data) # 磁盘 → GPU 直连启用 GDS 时这个Before/After转换同样记录在 code_transformation_patterns.md 的File IO to GPU with KvikIO一节中包含本地文件与 S3 URL 两个版本前者把numpy.fromfile()cp.asarray()的两次拷贝路径替换为一次 GDS 直读后者用RemoteFile.open_s3_url直接把 S3 对象读进 CuPy 数组。这也是该技能推荐的标准化改造模板。常见陷阱忘记设置线程池大小——默认只有 1 个线程。对大型文件kvikio.defaults.set({num_threads: 16})可显著提升吞吐。用 KvikIO 读结构化格式——不要用 KvikIO 读 CSV/Parquet/JSON应使用cudf.read_csv()、cudf.read_parquet()等KvikIO 只负责原始二进制数据。不检查 GDS 可用性——没有 GDS 时代码也能正常跑回退到 POSIX但拿不到完整带宽收益。用kvikio.cufile_driver.get(is_gds_available)确认。性能关键路径上未对齐 I/O——偏移与大小应使用 4 KiB 对齐以获得最佳 GDS 性能。不使用上下文管理器——始终用with kvikio.CuFile(...)确保文件被正确关闭与注销deregister。期望 RemoteFile 支持写入——RemoteFile是只读的。要写远程存储先写本地再用相应 SDK如 S3 用 boto3上传。Docker 中未配置 GDS——在 Docker 中以只读方式挂载/run/udev--volume /run/udev:/run/udev:ro否则 KvikIO 会静默回退到 POSIX。小结KvikIO 的价值在于把存储 ↔ 显存路径上的 CPU 中转彻底移除本地用CuFile、远程用RemoteFile、分块数组用GDSStore并通过kvikio.defaults与kvikio.cufile_driver完成运行时调优。配合 optimize-for-gpu 技能的整体方法先测量基线、再选最小改动方案、最后做同步基准验证KvikIO 是原始/远程文件 I/OGPU 化改造的标准答案。需要进一步了解它在整个 GPU 优化工具箱中的定位可继续阅读 decision_framework.md选型决策与 installation.md完整安装指引。【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000 scientists worldwide. 165 ready-to-use validated skills plus 100 scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表