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

写操作成本

写操作成本

Go
慕桂英4014372 2021-12-06 19:32:54
我有一个将字符串写入文件的 Go 程序。我有一个循环迭代20000 次,在每次迭代中,我将大约 20-30 个字符串写入文件。我只是想知道将它写入文件的最佳方法是什么。方法一:保持打开代码开头的文件指针,为每个字符串写入。它使其成为 20000*30 次写入操作。方法二:使用bytes.Buffer 把所有的东西都存储在buffer里,写在最后。同样在这种情况下文件指针应该从代码的开头打开还是在代码的结尾打开。有关系吗?我假设方法 2 应该工作得更好。有人可以用一个理由证实这一点。一次写作如何比定期写作更好。因为文件指针无论如何都会打开。 我正在使用f.WriteString(<string>)并且buffer.WriteString(<some string>)缓冲区是类型的bytes.Buffer并且f是打开的文件指针。
查看完整描述

3 回答

?
白猪掌柜的

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

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


与您的第二种方法相比 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)

}


查看完整回答
反对 回复 2021-12-06
?
米脂

TA贡献1836条经验 获得超3个赞

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

现在,您可能知道,您的操作系统不会直接写入文件,它使用内部内存缓存来保存已写入的文件并稍后执行真正的 I/O。我不知道那的确切细节,一般来说我不需要。

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

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



查看完整回答
反对 回复 2021-12-06
?
慕妹3242003

TA贡献1824条经验 获得超6个赞

系统调用并不便宜,所以第二种方法更好。

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

$ ./lat_syscall write
Simple write: 0.1522 microseconds

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


查看完整回答
反对 回复 2021-12-06
  • 3 回答
  • 0 关注
  • 218 浏览
慕课专栏
更多

添加回答

举报

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