【问题标题】:How to determine reasonable number of bytes to read per read system call?如何确定每次读取系统调用要读取的合理字节数?
【发布时间】:2016-06-03 04:52:47
【问题描述】:

我正在玩文件读/写,但很难决定为“读取”系统调用设置多大的读取缓冲区。

特别是,我正在查看“http://pubs.opengroup.org/onlinepubs/009695399/functions/read.html

除了 SSIZE_MAX 之外,似乎对我一次可以读取的字节数没有任何限制。

更糟糕的是,如果我创建一个包含 SSIZE_MAX 个字符的数组,程序会产生:

sh: ./codec: Bad file number

是否有任何合理的方法来决定每次读取系统调用读取多少字节?我担心这可能会因系统而异(我不能只进行尽可能多的读取,直到读取无法确定我可以读取的确切字节数,即使我这样做了,它也不一定会更快比读取更少的字节)。

我的一个想法是检查我的 CPU 缓存大小并尝试使我的缓冲区不大于该大小,但由于我不知道 CPU 缓存是如何工作的,所以我不确定这是否一定正确。

提前谢谢。

【问题讨论】:

  • “错误的文件号”听起来确实与指定的缓冲区大小有关,除非发生了其他事情。请注意,SSIZE_MAX 一般为far too large for a buffer
  • @user2864740 是的。 SSIZE_MAX 在 32 位系统上为 2 GB,在 64 位系统上为 8 EB。
  • 错误代码EBADFD(错误文件号)可能来自不同的来源,可能您忘记检查打开要读取的文件是否真的成功。
  • @Dmitry:如果您看到this 性能图,您认为合理的缓冲区大小是多少? (绿色是读,红色是写。)
  • @Dmitry:了解为什么如此复杂的另一种方法是认识到文件的不同部分甚至可能不在同一个存储上。例如,在 RAID(和 LVM?)系统上,某些部分可能分散在磁盘上,并且由于并行化或单个磁盘活动,读取不同部分可能需要不同的时间。或者,在虚拟磁盘(例如增量 VHD)上,文件的不同部分可以位于完全不相关的存储介质上。所以这是另一个复杂程度(人们通常会忽略),它说明了为什么 1 个数字是不够的。

标签: c io posix bufferstrategy


【解决方案1】:

我思考了基本相同的问题,得出了一个非常简单的结论:

使用保守的默认值或启发式,但如果用户愿意,可以轻松覆盖它。

您会看到,在某些情况下,用户可能不希望您的实用程序获得最大吞吐量,但可能会在后台执行任何操作。也许这项任务并不那么重要。就个人而言,在 Linux 中,我经常使用 niceionice 实用程序将长期但不优先的任务放在次要任务上,可以这么说,这样它们就不会干扰我的实际工作。

过去十年的基准表明,128k 到 2M 的块大小(217 到 221 字节)可以始终如一地运行良好——几乎在所有情况下都与最佳速率相差不远情况——平均值缓慢地移向该范围的较大端。通常,两个大小的幂似乎比非二的幂更好,尽管我还没有看到足够多的各种 RAID 配置的基准来完全相信这一点。

因为您的实用程序几乎肯定会针对每种新硬件类型/代重新编译,所以我更喜欢在编译时定义默认块大小,但在运行时将其覆盖(通过命令行选项) 、环境变量和/或配置文件)。

如果您的实用程序是为当前的 POSIXy 操作系统打包的,则二进制文件可以使用似乎最适合在该机器上完成的任务类型的默认值;例如,Raspberry Pis 和其他 SBC 通常没有那么多内存开始,因此较小的(例如,65536 字节)默认块大小可能效果最好。桌面用户可能不关心内存占用,因此您可以在当前桌面计算机上使用更大的默认块大小。

(服务器和高性能计算(这是我考虑过的地方),块大小基本上要么根据确切的硬件和工作负载进行基准测试,要么只是一个几乎不知情的猜测。通常是后者.)

或者,您可以基于所涉及文件的st_blksizes 构造一个启发式算法,可能乘以默认因子,并限制在某个首选范围内。然而,随着硬件的变化,这种启发式算法往往会快速比特腐烂。

对于启发式方法,重要的是要记住,这个想法并不总是达到最佳状态,而是要避免非常糟糕的结果。如果用户想要挤出最后百分之几的性能,他们可以在自己的工作流程中进行一些基准测试,并相应地调整默认值。 (我个人有,也有。)

【讨论】:

  • 只有一个警告:它甚至不需要 bit-rot 就可以使 st_blksize 无用。我见过太多的例子,有人在磁盘存储系统上构建类似 28 磁盘 RAID-5 阵列的东西,每个磁盘段大小为 1MB(must 越大越好!)领先到 27MB 的 RAID-5 条带大小。然后他们构建了一个文件系统,上面有 4kB 的块大小。
  • @AndrewHenle:没错!几年前,当我开始学习如何设置计算集群时,我自己也做了很多。知道我应该能够获得什么样的利率让我起初有点摸不着头脑,但最终我明白了为什么我没有达到它,以及如何正确地做到这一点(呃,更好)。我已经在台式机上使用(Linux 软件)RAID-0 和 RAID-1 多年,但在我对安装感到满意之前,我仍然需要相当多的时间来基准测试和测试更大的 RAID-5 阵列:太多了变量(硬件、设置、文件系统、不同的需求)。
【解决方案2】:

在您要阅读的文件上调用stat()fstat()struct stat 成员 st_blksize 包含用于从名为 stat() 的文件中读取的最佳缓冲区大小。

【讨论】:

  • -1 嗯,这是最佳块大小,而不是最佳缓冲区大小。它旨在最小化读取-修改-写入,而不是最大化性能。您想要处理的数据越多,您的缓冲区就应该越大(实际上,没有像 最佳缓冲区大小这样的东西)。
  • 我会将我的问题更正为“合理的缓冲区大小”。
  • @Dmitry:好的,但请注意,我认为合理的缓冲区大小比st_blksize...如果st_blksize 是 64K,那么合理的缓冲区大小可能是 1M。
  • @Mehrdad OP 询问read() 系统调用的正确缓冲区大小(参见他问题的第一句话)。 st_blksize 的字面意思是 documented 作为 “此对象的特定于文件系统的首选 I/O 块大小。在某些文件系统类型中,这可能因文件而异。” 为什么您认为这不是正确答案?
  • @FUZxxl:因为如果您阅读 this documentation,它会说 st_blksize 字段给出了“首选”大小,以实现高效的文件系统 I/O。 (以较小的块写入文件可能会导致效率低下 read-modify-rewrite。)",即 (1) 它旨在避免磨损驱动器,而不是最大化您的吞吐量, 和 (2) 它是一个块大小,这意味着您的缓冲区大小应该只是它的 倍数
【解决方案3】:

好吧,确定合适的缓冲区大小完全取决于问题。首先,我将检查缓冲区大小确定的最新技术:stdio 使用 BUFSZ 作为缓冲区大小(通常是一个 unix 磁盘块的大小,曾经固定为 512,现在可能有点介于 1024 和 4096 之间——从磁盘块大小到虚拟页面大小——)对于在此处移动的数量来说,这远远太低,但这是一个很好(并且认为)可以接受的值。

另一方面,想想一个只有 8Kb 内存并使用 1 兆字节缓冲存储的嵌入式系统。使用虚拟内存进行缓冲存储听起来有些奇怪(如果允许的话)。

假设您正在设计一个文件复制实用程序,其中最好的缓冲区大小确定将是最好的。可能,认为最大的可接受值是必须的。但是经过一些测试后,您会发现很多误用的内存。假设您设计您的流程以仅使一个线程充当读取器和写入器。您读取数据,然后将该数据写入另一个文件。出现的第一件事是您使用的内存量不会影响,因为这只会影响进程中写入和读取的顺序......如果一次读取意味着磁盘读取(假设一次一个块)超过磁盘块大小的东西不会让您进行额外的读取以获取相同的数据(这实际上是在系统级别完成的,它可以缓冲您的数据,从而可以在一个单字节块中读取数据,而系统正在逐块读取数据)

第二种方法是最小化系统调用。在这一点上,每个人都知道进行系统调用是一件很昂贵的事情,所以如果我们可以安排进行最少的系统调用,我们会得到一些好处。但是一段时间后,您会发现没有额外的性能,因为系统正在逐块读取您的数据,而您的进程正在等待它,这使得系统调用惩罚完全不可见,因为它代表不到 1%的等待时间。此外,系统必须保证您的数据不会同时发生变化,同时进行原子读取调用(这是通过从头到尾锁定文件的 inode 来完成的),因此没有进程实际上可以引用同一个文件直到您完成通话。允许较大的缓冲区会使您的进程可能太大而无法放入内存并使用额外的交换处理加载您的系统。这让你在去大缓冲区之前要小心。

最后,额外的系统调用惩罚与使用大缓冲区所产生的惩罚相比实在是太小了,根本没有用处。如果您生活在一个正常大小的系统中(假设一台笔记本电脑或台式电脑,其内存数量约为 2-8Gb),那么 8Kb 的缓冲区大小可能适用于所有场景。

最后要考虑的是音频/视频流缓冲区大小的确定。在这种情况下,通常有一个生产者(要复制的数据的读取者)以不同的速度(例如,在网络负载上随时间变化)产生数据,以及一个消费者以固定的速率(比如说 8kbps telco call、192kbps for a cd play 等)缓冲区大小必须允许补偿数据提供速度的变化以不清空缓冲区,因为那时,我们将有一个无声音时段来填补空白。这本质上是动态的,您必须预先计算可能的网络数据包丢失和重传容限。通过延迟消费者并用流数据填充一些缓冲区,您可以补偿并使数据消费者满意而不会丢失数据。在这种情况下,2Mb 的流缓冲区在某些场景下是常识,具体取决于数据丢失的概率、重传时间和您想要承受的流质量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-17
    • 2015-04-16
    相关资源
    最近更新 更多