3

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

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

除了 SSIZE_MAX 之外,它似乎没有说明我一次可以读取多少字节。

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

sh: ./codec: Bad file number

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

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

提前谢谢。

4

3 回答 3

5

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

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

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

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

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

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

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

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

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

于 2016-02-22T01:53:25.383 回答
0

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

于 2016-02-21T21:24:32.420 回答
0

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

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

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

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

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

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

于 2016-02-24T06:45:29.883 回答