我正在使用 Tensorflow 的 TFRecords 格式,它将一堆数据点序列化到一个大文件中。这里的典型值是每个数据点 10KB,每个大文件 10,000 个数据点,一个大文件大约 100MB。 TFRecord 通常只写入一次 - 它们不会被附加。我思考这意味着它们不会非常分散。
我相信 TFRecords 是基于 Google 的内部 RecordIO 格式。
通常人们在 Ubuntu 18.04 或 20.04 上运行 Tensorflow 和 TFRecords,我认为这通常是 ext4 文件系统。
通常,深度学习工程师在 SSD/NVME 磁盘上运行。与 GPU 本身的巨大成本相比,磁力旋转盘的成本增量并不重要。
问题一:
在 ext4 文件系统中,如果我知道某个特定数据点是文件中的 9,000,000 字节,我可以查找该位置并开始在恒定时间内读取该数据点吗?我所说的恒定时间只是作为搜索深度的函数。我不担心文件总大小的影响。
如果这是真的,则意味着 ext4 文件系统中的每个文件都有某种查找表/索引,将查找位置映射到磁盘扇区。
我已经几十年没有研究过文件系统了,但我似乎记得 FAT 文件系统是链表——你必须读取一个磁盘扇区才能知道下一个磁盘扇区是什么。这意味着要在文件中查找 9,000,000 字节,我需要从前 8,999,999 字节读取所有磁盘扇区。例如,寻道时间与寻道的“深度”成线性关系。我希望 ext4 是恒定时间,而不是线性的。
问题2:
我的最终目标是对 TFRecord 执行随机访问。由于我认为与优化磁旋转盘片的读取速度有关的原因,TFRecord 是为串行读取而不是随机访问而设计的。
无论查找函数是否是恒定时间(作为查找深度的函数),随机访问 ext4 文件系统上的大文件是否“足够快”?老实说,我并不确切知道足够快的速度是多少,但为了简单起见,假设一个非常快的深度学习模型可能每秒能够提取 10000 个数据点,其中每个数据点约为 10KB,并从一个大文件中随机提取。
答案1
也许,如果文件在磁盘上没有碎片的话。但如果时间严格恒定,可能并不重要。
ext2 和 ext3 将数据块的位置存储在具有 1 到 4 层的树中,因此查找不能是恒定时间的。此外,树的块原则上可以位于文件系统的任何位置,因此可能需要一些磁盘查找。
ext4 存储一个范围树,然后描述多个连续的数据块。因此,如果已知文件只有一个范围,则查找时间将是恒定的。但如果它是碎片化的(或大于 128 MiB,需要多个范围),则不会。
(来源:https://www.kernel.org/doc/html/latest/filesystems/ext4/dynamic.html#the-contents-of-inode-i-block)
虽然我可能更担心查找是否足够快,比如果他们是恒定时间。这是一个更容易的目标,因为无论如何树都不会太深,并且如果您重复访问同一个文件,它们很快就会全部加载到内存中,从而消除任何磁盘搜索(这不是SSD 上的一个大问题,但是,无论如何)。每次访问还会产生系统调用开销,如果您在每次读/写之前进行查找,则会增加两倍。尽管我认为有一些更高级的系统调用可以缓解这种情况。
FAT 文件系统是链表——你必须读取一个磁盘扇区才能知道下一个磁盘扇区是什么。这意味着要在文件中查找 9,000,000 字节,我需要从前 8,999,999 字节读取所有磁盘扇区。例如,寻道时间与寻道的“深度”成线性关系。
FAT 文件系统有一个块指针表(FAT 表),这些指针形成链表。不是数据块本身。因此,例如,对于 4 kB 的块大小,您只需要读取 9000000 / 4096 ~= 2000 个指针,价值几 kB。但它仍然是一个链表,迭代它需要与查找位置成比例的许多步骤(除非 fs 驱动程序有一些智能来减少它)。但 FAT 表是连续的,并且也可能位于内存中,因此不涉及磁盘查找。
这里的典型值是每个数据点 10KB,每个大文件 10,000 个数据点,一个大文件大约 100MB。
假设一个非常快的深度学习模型可能能够每秒提取 10000 个数据点,其中每个数据点约为 10KB,并且从大文件中随机提取。
一个 100 MB 的文件应该很容易完全适合内存(多次),并且将其保留在那里也可以消除搜索的系统调用开销。如果你只是读书,那就是这样。
如果您也要写入,请注意,无论如何,并非所有写入都会立即到达磁盘闪存,而无需特别注意,这可能会减慢整个过程。 (至少您需要fsync()
每次都调用,并相信驱动器不会欺骗您。)将文件存储在内存中,您可以每隔一段时间手动将其写回,或者将其映射到内存mmap()
并调用msync()
要求不时回信。