我知道重用缓冲区很方便,而不是每次使用io.Copy时都进行分配。然而,在多次打印其值后,我得到了全零,缓冲区的大小从未改变。我试着把尺寸设置为8和1。
与此相关的是,我应该将缓冲区大小设置为什么值?
io.CopyBuffer()
文档:
func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)
CopyBuffer与Copy相同,只是它通过提供的缓冲区(如果需要的话)分段,而不是分配临时缓冲区。如果buf为零,则分配一个;否则,如果它的长度为零,CopyBuffer就会死机。
如果src实现WriterTo或dst实现ReaderFrom,则不会使用buf执行复制。
因此io.CopyBuffer()
将数据(字节)从src
复制到dst
。源是io.Reader
,目的地是io.Writer
。这些接口允许您读取和写入字节片([]byte
)。
在进行复制的一般情况下,我们需要从源读取一个切片,然后将其写入目标。所以io.CopyBuffer()
需要一个缓冲区。buf
参数允许您传递一个字节片(如果您已经有),如果您这样做了,那么该缓冲区将用于执行任务,因此不必分配新的片(在操作结束时会丢弃)。
它应该是多大尺寸?越大越好,但不需要比要复制的数据更大的数据。显然,越大需要更多的内存,所以需要权衡。通常,几KB是一个很好的折衷方案。
请注意,如文档所述,如果源实现io.WriterTo
或目标实现io.ReaderFrom
,则这些接口允许读取/在不必传递切片的情况下进行写入,因此在这种情况下,将不会使用您传递的缓冲区。如本例所示:
srcData := []byte{1, 2, 3, 4, 5, 6, 7}
src := bytes.NewBuffer(srcData)
dst := &bytes.Buffer{}
buf := make([]byte, 10)
io.CopyBuffer(dst, src, buf)
fmt.Println(srcData)
fmt.Println(dst.Bytes())
fmt.Println(buf)
哪些输出(在Go Playground上尝试):
[1 2 3 4 5 6 7]
[1 2 3 4 5 6 7]
[0 0 0 0 0 0 0 0 0 0]
由于我们使用bytes.Buffer
作为源和目的地(并且它同时实现了io.ReaderFrom
和io.WriterTo
),因此不使用缓冲区。
让我们构造一个不实现这些接口的源和目标,这样我们就可以测试是否/如何使用我们传递的缓冲区。
为此,我将在结构中嵌入*bytes.Buffer
,但指定WriteTo
和ReadFrom
字段,因此这些方法不会从嵌入的bytes.Buffer
:中升级
srcData := []byte{1, 2, 3, 4, 5, 6, 7}
src := struct {
WriteTo int // "disable" WriteTo method
*bytes.Buffer
}{0, bytes.NewBuffer(srcData)}
dst := struct {
ReadFrom int // "disable" ReadFrom method
*bytes.Buffer
}{0, &bytes.Buffer{}}
buf := make([]byte, 10)
io.CopyBuffer(dst, src, buf)
fmt.Println(srcData)
fmt.Println(dst.Bytes())
fmt.Println(buf)
这将输出(在Go Playground上尝试):
[1 2 3 4 5 6 7]
[1 2 3 4 5 6 7]
[1 2 3 4 5 6 7 0 0 0]
正如您所看到的,源中的数据被读取到缓冲区中,然后被写入目标。
请注意,您可能会传递一个小于要复制的数据的缓冲区,在这种情况下,读取/写入将在几次迭代中完成。在这种情况下,缓冲区中的数据可以只保存最后一次迭代,并且可以只保存部分数据(如果复制的大小不是缓冲区大小的整数倍)。它还取决于Read()
方法如何在源上实现,因为Read()
不需要读取传递给它的完整切片
还要注意,io.CopyBuffer()
不会记录写入传递缓冲区的数据被保留,它可能会被清除/归零。尽管出于性能原因没有实现此清除,但您不应指望它在io.CopyBuffer()
返回后保持有效数据。
在Go中使用io.Copy时,提供缓冲区可以通过减少每次读写操作所需的系统调用次数来提高性能。但是,缓冲区大小并不能决定将要复制的数据的大小。相反,缓冲区大小会影响复制过程的效率。
缓冲区大小通常是基于预期的输入/输出大小和底层系统的特性来选择的。选择缓冲区大小没有固定的规则,因为它取决于各种因素,如正在处理的数据的性质、可用内存以及特定用例的性能要求。
如果缓冲区太小,可能会导致频繁的缓冲区刷新,并降低潜在的性能提升。另一方面,如果缓冲区大小过大,可能会导致不必要的内存消耗。
要确定合适的缓冲区大小,可以考虑以下指导原则:
- 从一个合理的默认大小开始,例如4096(4KB),这是一个常见的选择
- 衡量不同缓冲区大小的代码的性能。您可以使用Go的测试包或基准测试实用程序等工具来比较执行时间和资源利用率
- 根据结果调整缓冲区大小。如果增加缓冲区大小可以提高性能,则可以尝试较大的值。如果减小它没有显著影响,可以尝试较小的值
请记住,缓冲区大小与要复制的数据的大小没有直接关系,而是会影响复制过程的效率。实验和性能评测可以帮助您确定特定用例的最佳缓冲区大小。