为什么Go中首选长度为512(又名[512]字节)的缓冲区字节数组进行读写



我在学习Jan Newmarch的"Network programming with Go"中的Go,我注意到他几乎所有的例子都涉及[512]字节作为写入和读取连接的缓冲区。

我试着在网上搜索,但没有得到答案。我怀疑这可能与I/o有关,但不确定这种设计背后的确切原因是什么。

有人能详细说明一下缓冲区的选择吗?

书中的一些示例代码:

func handleConn(c net.Conn){ 
defer c.Close()
var buf [512]byte 
for{ 
n, err := c.Read(buf[0:])
if err != nil{ return }
_, err2 := c.Write(buf[0:]) 
if err2 != nil{ 
return 
}
}
}

这不是一个直接的答案,但除了其他人在评论中所说的之外,还有一些背景。

包装文件和套接字的Go类型相对较薄,因为对它们进行的任何Read()Write()调用都会导致执行系统调用(对于套接字,这更为棘手,因为它们通过系统提供的轮询器(如epollkqueueIOCP等)使用异步I/O)。这意味着通过1字节的块从文件或网络中读取是非常无效的。

考虑另一个极端,可以分配一个100MiB缓冲区,并尝试将其传递给Read()。虽然内核的系统调用会很乐意接受这样大小的目的地,但应该注意的是,当代操作系统在网络套接字上的内部缓冲区大小约为64KiB1,因此在大多数情况下,Read()调用在读取了那么多或更少的数据后会返回。这意味着您将浪费大部分缓冲空间。

现在还有一组注意事项:从应用程序的套接字读取数据的模式是什么?

比方说,当您将数据从套接字流式传输到打开的文件时,您并不真正关心缓冲(您希望由其他人决定选择"正确"的大小)。对于这种情况,只需使用io.Copy()(目前(Go 1.6)使用32KiB的内部缓冲区)。

相反,如果您使用TCP作为传输来解析某些应用程序级协议,则通常需要读取任意固定大小的数据块。对于这种情况,最好的模式是用bufio.Reader—解决上述"小阅读"问题;然后使用io.ReadFull()将数据读取到所需大小的本地阵列/切片中(如果可能,请重用阵列和切片以降低垃圾收集器的压力)。

另一种情况是基于文本的"线条化"协议,如SMTPHTTP。在这些协议中,最大线路长度通常是固定的,使用协议线路的最大大小的缓冲区来处理它们是有意义的。(但无论如何,要处理这样的协议,最好使用net/textproto标准包。)

至于你的问题本身,我认为512只是一个美丽的数字,没有特别的意义。当你写这样一本书时,你无论如何都必须选择一些价值。

正如你从我对网络阅读实际工作模式的描述中看到的那样,大多数时候你根本不需要处理缓冲问题;让标准工具为您完成这项工作。只有当您面临标准包提供的默认值的实际问题时,才应该考虑调整这些内容。

TL;博士

  • 你正在读的书只是向你解释了基本概念,所以它必须使用一些数字
  • 现实世界中的代码似乎在需要缓冲时使用其他数字(通常更高)…
  • …但在绝对必要之前,你不应该关心这些数字:尽可能使用现成的工具

1当然,我不能说所有的操作系统都有不同的旋钮来调整这些东西,而"现代"可能在一年或更短的时间内开始意味着不同的东西,你知道…我仍然认为我的估计与事实非常接近。

最新更新