NVMe磁盘4GB与32MB文件pread随机4K直读延迟差异原因问询
我在NVMe磁盘上通过Direct IO(O_DIRECT)与pread接口,对4GB和32MB文件分别进行单4K页随机循环读取测试。结果显示4GB文件每页延迟约41微秒,32MB文件则约79微秒,请问该差异的合理原因是什么?
测试代码片段:
int b_fd = open("large.txt", O_RDONLY | O_DIRECT); int s_fd = open("small.txt", O_RDONLY | O_DIRECT); std::srand(std::time(nullptr)); void *buf; int ps = getpagesize(); posix_memalign(&buf, ps, page_size); long long nano_seconds = 0; // 随机读取的页数 int iter = 256 * 100; for (int i = 0; i < iter; i++) { int page_index = std::rand() % big_file_pages; auto start = std::chrono::steady_clock::now(); pread(b_fd, buf, page_size, page_index * page_size); auto end = std::chrono::steady_clock::now(); nano_seconds += std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count(); } std::cout << "large file average random 1 page direct IO read in nanoseconds is:" << nano_seconds/iter << "\n";
这种延迟差异核心源于NVMe SSD的物理特性、文件系统分配策略以及控制器的调度机制,具体可以拆解为以下几点:
多通道并行利用效率差异
NVMe SSD依赖多闪存颗粒通道实现高并行性。4GB文件体积远大于SSD的最小连续分配单元(如块组、物理页块),文件系统会将其分散分配到多个独立通道的物理块上。随机读时,控制器可以同时调度多个通道的IO请求,分摊单IO的延迟;而32MB文件体积过小,大概率被限制在少数甚至单个通道的物理块中,无法充分利用NVMe的多通道并行优势,单IO延迟自然更高。物理块碎片化与GC干扰
NVMe SSD的垃圾回收(GC)机制会定期回收包含无效页的物理块。小文件占用的物理块数量少,一旦其中某个块触发GC(比如有其他文件的无效页在同一块),随机读请求命中该块时会被GC操作阻塞,导致延迟陡增;而大文件占用的物理块数量多,单个块的GC操作对整体随机读的影响被稀释,且大文件通常被分配为连续块,GC触发频率更低。LBA-PBA映射缓存命中率
NVMe控制器会缓存逻辑块地址(LBA)到物理块地址(PBA)的映射表。4GB文件的LBA范围更广,随机读的分散性会让控制器的映射缓存更充分地被利用(缓存条目覆盖更多常用LBA);而小文件的LBA范围窄,若其物理块因多次分配/回收出现映射碎片化,缓存命中率反而更低,每次读都需要额外的地址转换开销。文件系统分配策略倾向
主流文件系统(如ext4、XFS)对大文件会优先分配连续的物理块组,这些块组通常分布在不同的SSD通道上;而小文件常被分配到零散的空闲块,甚至与其他小文件共享块组,导致IO请求集中在局部通道,加剧竞争延迟。
内容的提问来源于stack exchange,提问作者Kenneth

