写操作费用[Go lang]

问题描述 投票:5回答:3

我有一个Go程序,它将字符串写入文件。我有一个迭代20000次的循环,在每次迭代中我将大约20-30个字符串写入文件。我只是想知道哪个是将其写入文件的最佳方式。

  • 方法1:在代码的开头保持打开文件指针,并为每个字符串写入。它使它成为20000 * 30的写操作。
  • 方法2:使用bytes.Buffer Go并将所有内容存储在缓冲区中并在末尾写入。在这种情况下,文件指针应该从代码的开头或代码的末尾打开。有关系吗?

我假设方法2应该更好。有人可以用一个理由证实这一点。如何立即写作比定期写作更好。因为文件指针无论如何都会打开。我使用f.WriteString(<string>)buffer.WriteString(<some string>)缓冲区是bytes.Buffer类型和f是文件指针打开。

performance go io
3个回答
7
投票

bufio包是为这种任务创建的。在进行系统调用之前,bufio.Writer不会为每个Write调用进行系统调用,而是在内部存储器中缓冲固定数量的字节。在系统调用之后,内部缓冲区将重用于下一部分数据

与您的第二种方法bufio.Writer相比

  • 制作更多的系统调用(N/S而不是1
  • 使用更少的内存(S字节而不是N字节)

其中S - 是缓冲区大小(可以通过bufio.NewWriterSize指定),N - 需要写入的数据的总大小。

用法示例(https://play.golang.org/p/AvBE1d6wpT):

f, err := os.Create("file.txt")
if err != nil {
    log.Fatal(err)
}
defer f.Close()

w := bufio.NewWriter(f)
fmt.Fprint(w, "Hello, ")
fmt.Fprint(w, "world!")
err = w.Flush() // Don't forget to flush!
if err != nil {
    log.Fatal(err)
}

3
投票

写入文件时花费时间的操作是系统调用和磁盘I / O.文件指针打开的事实不会花费任何成本。天真地说,我们可以说第二种方法是最好的。

现在,正如您所知,您的操作系统不直接写入文件,它使用内部内存缓存来处理写入的文件,并在以后执行真正的I / O.我不知道具体细节,一般来说我不需要。

我建议的是一个中间解决方案:为每个循环迭代做一个缓冲,然后写这个N次。这样可以减少系统调用和(可能)磁盘写入的大部分,但不会消耗过多的缓冲区内存(依赖于字符串的大小,我应该考虑到这一点)。

我建议为最佳解决方案进行基准测试,但由于系统进行了缓存,基准磁盘I / O是一个真正的噩梦。


1
投票

Syscalls并不便宜,所以第二种方法更好。

您可以使用lmbench中的lat_syscall工具来测量调用单个write所需的时间:

$ ./lat_syscall write
Simple write: 0.1522 microseconds

因此,在我的系统上,每个字符串调用write需要大约20000 *0.15μs= 3ms的额外时间。

© www.soinside.com 2019 - 2024. All rights reserved.