Go语言 在io.Copybuffer(...)中buffer(最后一个参数)到底是什么?

f8rj6qna  于 2023-08-01  发布在  Go
关注(0)|答案(2)|浏览(125)

我知道重用缓冲区比每次使用io.Copy分配缓冲区更方便。然而,在多次打印它的值之后,我得到的都是零,而且缓冲区的大小从未改变。我试着把大小设置为8和1。
与此相关的是,我应该将缓冲区大小设置为什么值?

oxcyiej7

oxcyiej71#

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.ReaderFromio.WriterTo),因此不使用缓冲区。
让我们构造一个不实现这些接口的源和目标,以便测试是否/如何使用我们传递的缓冲区。
为此,我将在一个结构中嵌入*bytes.Buffer,但指定一个WriteToReadFrom字段,因此这些方法将不会从嵌入的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()返回后保存有效数据。

oprakyz7

oprakyz72#

在Go中使用io.Copy时,提供缓冲区可以通过减少每个读写操作所需的系统调用数量来提高性能。但是,缓冲区大小并不决定要复制的数据的大小。相反,缓冲区大小影响复制过程的效率。
通常基于预期的输入/输出大小和底层系统的特性来选择缓冲器大小。选择缓冲区大小没有固定的规则,因为它取决于各种因素,例如正在处理的数据的性质,可用内存以及特定用例的性能要求。
如果缓冲区大小太小,则可能导致频繁的缓冲区刷新并降低潜在的性能增益。另一方面,如果缓冲区大小太大,则可能导致不必要的存储器消耗。
要确定适当的缓冲区大小,可以考虑以下准则:
1.从一个合理的默认大小开始,例如4096(4 KB),这是一个常见的选择。
1.使用不同的缓冲区大小测量代码的性能。您可以使用Go的测试包或基准测试实用程序等工具来比较执行时间和资源利用率。
1.根据结果调整缓冲区大小。如果增加缓冲区大小可以提高性能,则可以尝试更大的值。如果减小该值没有显著影响,则可以尝试较小的值。
请记住,缓冲区大小与要复制的数据大小没有直接关系,而是影响复制过程的效率。实验和性能分析可以帮助您确定特定用例的最佳缓冲区大小。

相关问题