版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | http://vearne.cc

前言

大家都知道写文件时数据流转的顺序是

用户空间文件缓冲区 -> 内核空间文件缓冲区 -> 内核空间IO队列

29075379_138130814277tC.jpg-63.6kB

默认的ANSI C库,对用户空间文件缓冲区有三种方式

  • 全缓冲
  • 行缓冲
  • 无缓冲

难道Golang没有?笔者不敢断定,但有以下的对比试验或许能说明些问题 write.go

package main

import (
	"os"
	"log"
	"time"
)

func main() {
	file, err := os.OpenFile("test.txt",
		os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		log.Fatal(err)
	}
	for i := 0; i < 1000; i++ {
		time.Sleep(2 * time.Second)
		file.Write([]byte("xxxx"))
	}
	file.Close()
}

write2.go write2.go 使用了标准库提供的bufio, 给写文件增加了4096byte的缓冲区

package main

import (
	"os"
	"log"
	"time"
	"bufio"
)


func main(){
	file, err := os.OpenFile("test.txt",
		os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666)
	writer := bufio.NewWriterSize(file, 4096)
	if err!= nil{
		log.Fatal(err)
	}
	for i:=0;i<1000;i++{
		time.Sleep(2 * time.Second)
		writer.Write([]byte("xxxx"))
	}
	writer.Flush()
	file.Close()
}

使用strace跟踪系统调用, 在没有使用bufio的情况如下

) = -1 ETIMEDOUT (Connection timed out)
futex(0x53a1d0, FUTEX_WAKE, 1)          = 1
futex(0xc420072148, FUTEX_WAKE, 1)      = 1
write(3, "xxxx", 4)                     = 4
futex(0x53cb00, FUTEX_WAIT, 0, {1, 999998648}


) = -1 ETIMEDOUT (Connection timed out)
futex(0x53a1d0, FUTEX_WAKE, 1)          = 1
futex(0xc420046948, FUTEX_WAKE, 1)      = 1
write(3, "xxxx", 4)                     = 4
futex(0xc420046948, FUTEX_WAKE, 1)      = 1
futex(0x53cb80, FUTEX_WAIT, 0, {1, 999933913}

每次调用writer.Write([]byte(“xxxx”)) 均有系统调用write(3, “xxxx”, 4) 被触发

使用bufio的情况如下

) = -1 ETIMEDOUT (Connection timed out)
futex(0x53c290, FUTEX_WAKE, 1)          = 1
futex(0xc42006a148, FUTEX_WAKE, 1)      = 1
futex(0x53ebc0, FUTEX_WAIT, 0, {2, 112782}

) = -1 ETIMEDOUT (Connection timed out)
futex(0x53c290, FUTEX_WAKE, 1)          = 1
futex(0xc42006a148, FUTEX_WAKE, 1)      = 1
futex(0xc42006a148, FUTEX_WAKE, 1)      = 1
futex(0x53eb40, FUTEX_WAIT, 0, {1, 999998437}

在用户空间的文件写缓冲区没有写满前,没有write()系统调用被触发,可见使用用户空间文件缓冲区是有效的。

总结

系统调用的开销很高,如果能减少一部分系统调用能够有效的提高服务的吞吐能力。看来以后再读写文件时,很有必要多使用bufio开启用户空间的文件读写缓冲区。

参考资料

  1. golang中bufio包的实现原理

请我喝瓶饮料

微信支付码