要估计fseek 的延迟,我们应该将其分为两部分:软件工作和硬件寻道时间延迟。软件工作是 ext4 文件系统(FS,在 Linux 中这是内核的 VFS 子系统)的实现,它将向硬件块存储设备生成几个“随机”请求(I/O 操作)。硬件将花费一些时间来处理每个随机请求。
经典的 UNIX 文件系统 (UFS/FFS) 和基于它们设计的 linux 文件系统,使用 superblock 来描述磁盘上的 fs 布局,将文件存储为inodes(已知位置有 inode 数组),并存储文件数据以固定大小的块(在 Linux 中最多 4KB)。要从文件名中查找 inode,操作系统必须读取超级块,查找路径中的每个目录,从目录中读取数据以查找文件的 inode 编号(ls -i 将显示当前目录的 inode)。然后,使用来自 superblock OS 的数据可以计算出 inode 的存储位置并读取 inode。
inode 包含文件数据块的列表,通常是树状结构,检查https://en.wikipedia.org/wiki/Inode_pointer_structure 或http://e2fsprogs.sourceforge.net/ext2intro.html
文件的第一部分,几十KB存储在块中,直接在inode中列出(直接块; ext2/3/4中的12个)。对于较大的文件,inode 具有指向具有文件块列表(间接寻址块)的一个块的指针。如果文件较大,则使用 inode 中的 next 指针来描述“双重间接块”。它指向枚举其他块的块,每个块都包含指向具有实际数据的块的指针。有时需要三重间接块指针。这些树相当有效,在每一层都有大约 512 的度数(4KB 块,每个指针 8 个字节)。因此,要从文件中间访问数据,ext2/3/4 可能会产生多达 4-5 个低级 I/O 请求(超级块缓存在 RAM 中,inode 也可能缓存)。这些请求在地址中没有结果,因此它们几乎是对块设备的随机搜索。
Linux FS(ext4、XFS)的现代变体对大文件存储进行了优化,称为范围(https://en.wikipedia.org/wiki/Extent_(file_systems))。 Extents 允许 FS 将文件放置描述为不是块列表,而是文件片段/指针对的数组(start_block,number_of_consequent_blocks)。每个片段可能从 MB 的一部分到 128 MB。 4个第一个extents存储在inode中,更多extents再次存储为树状结构。因此,对于范围,您可能需要 2-4 次随机 I/O 操作才能访问文件的中间部分。
HDD 对随机请求的访问时间很慢,因为它们应该物理地将标头移动到正确的圆形轨道上(并且将标头准确地定位在轨道上;这需要进行一些旋转,例如 1/8 或 1/16 来完成)然后等待磁盘(盘片)最多旋转 1 次以获取请求的部分轨道。 HDD 的典型转速为 5400 和 7200 rpm(revolutions per minute、90 rps 和 120 rps)或高速企业 HDD - 10000 rpm 和 15000 rpm(160 rps 和 250 rps)。因此,从磁盘的随机位置获取数据所需的平均时间约为 0.7-1 转,而对于典型的 7200 rpm 硬盘 (120rps),它约为 1/120 秒 = 8 毫秒(毫秒)= 0.008 秒。每个随机请求都需要 8 毫秒来获取数据,在您的情况下最多有 4-5 个随机请求,因此使用 HDD 您可能会期望时间达到 接近 40 毫秒 在文件中查找。 (第一次查找会花费更多,下一次查找可能会更便宜,因为块指针树的某些部分被操作系统缓存;查找几个下一个块非常便宜,因为 linux 可以在请求第一次查找后读取它们)。
SSD 没有旋转或移动部件,SSD 上的任何请求都以相同的方式处理。 SSD 控制器使用自己的转换表将请求的块 ID 解析为内部 nand 芯片+块 ID,然后读取真实数据。从 NAND 读取数据会检查纠错码,有时需要多次内部重读才能正确读取块。在便宜的 NAND 类型中读取速度较慢 - 每个单元中存储 3 位数据的 TLC - 8 级; MLC 速度更快 - 2 位数据,4 级;在具有 1 位且只有 2 级的不存在的 SLC SSD 中非常快。在磨损的 SSD 或固件中存在错误(错误的电池电荷退化模型)的 SSD 中,读取速度也较慢。
SSD 中这种随机访问的速度非常快,它们通常被声明为 SSD 规格,例如 50000 - 100000 IOPS(I/O operations per second,通常为 4KB)。可能会为更深的队列声明高 IOPS 计数,因此 SSD(使用 QD1)的实际平均随机读取延迟为 200-300 microseconds 每个请求(0.2 - 0.3 毫秒;在 2014 年;部分延迟是慢 SATA/SCSI 仿真;NVMe SSD 可能更快,因为它们使用更简单的软件堆栈)。通过我们的 4-5 次请求,我们可以将 fseek on SSD 估计为几毫秒,例如最多 1 - 1.5 毫秒 或有时更长。
您可以使用strace -T ./your_fseek_program 检查fseek 所需的时间。它将报告执行每个系统调用所需的时间。但是要获得真正的延迟,您不仅应该检查寻道时间,还应该检查下一个read 系统调用的时间。在每次运行此测试之前,您可能需要使用来自根 (https://unix.stackexchange.com/questions/17936/setting-proc-sys-vm-drop-caches-to-clear-cache) 的 echo 3 > /proc/sys/vm/drop_caches 命令刷新内核缓存。
您也可以尝试一些 I/O 基准测试,例如 iozone、iometer 或 fio 来估计搜索延迟。