为了账号安全,请及时绑定邮箱和手机立即绑定

为什么在 Go 中首选长度为 512 的缓冲区字节数组,即 [512]byte 进行读写?

为什么在 Go 中首选长度为 512 的缓冲区字节数组,即 [512]byte 进行读写?

Go
小怪兽爱吃肉 2022-01-17 17:44:45
我正在从 Jan Newmarch 的“使用 Go 进行网络编程”中学习 Go,我注意到几乎所有他的示例都涉及 [512]byte 作为写入和读取连接的缓冲区。我试图在网上搜索,但没有得到答案。我怀疑它可能与 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          }    } }
查看完整描述

1 回答

?
白猪掌柜的

TA贡献1893条经验 获得超10个赞

不是直接的答案,而是一些背景以及其他人在评论中所说的内容。

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

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

现在出现了另一组考虑因素:您的应用程序从套接字读取数据的模式是什么?

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

相反,如果您正在解析一些使用 TCP 作为其传输的应用程序级协议,您通常需要以任意固定大小的块读取数据。对于这种情况,最好的模式是将套接字包装在一个bufio.Reader- 以解决上面概述的“小读取”问题 - 然后用于io.ReadFull()将数据读取到您需要大小的本地数组/切片中(如果可能,请重用您的数组和切片以降低垃圾收集器的压力)。

另一种情况是基于文本的“逐行”协议,例如SMTPor HTTP。在这些协议中,最大行长度通常是固定的,因此使用协议行的最大大小的缓冲区来处理它们是有意义的。(但无论如何,要处理这样的协议,最好使用net/textproto标准包。)

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

从我对从网络读取的实际工作模式的描述中可以看出,大多数时候你根本没有处理缓冲的事务——让标准工具为你做这件事。当您遇到标准包提供的默认值的真正问题时,您应该只考虑调整这些东西。

TL; 博士

  • 您正在阅读的书只是向您解释了基本概念,因此必须使用一些数字。

  • 实际代码在需要缓冲时似乎使用其他数字(通常更高)......

  • ……但除非绝对必要,否则您不应该关心这些数字:尽可能使用现成的工具。


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


查看完整回答
反对 回复 2022-01-17
  • 1 回答
  • 0 关注
  • 168 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信