go在提供了io包的同时也提供了bufio包来实现有缓存的读写操作以提高读写性能。为什么bufio性能比io高呢?
缓冲读写缓冲读// 默认缓冲区大小const ( defaultBufSize = 4096)// 最小缓冲区大小 自定义小于次阈值将会被覆盖const minReadBufferSize = 16// 使用默认缓冲区大小bufio.NewReader(rd io.Reader)// 使用自定义缓冲区大小bufio.NewReaderSize(rd io.Reader, size int)缓冲读的大致过程如下,设定好缓冲区大小buf_size后,读取的字节数为rn,缓冲的字节数为bn:
如果缓冲区为空,且 rn >= buf_size,则直接从文件读取,不启用缓冲。如果缓冲区为空,且 rn < buf_size,则从文件读取buf_size 字节的内容到缓冲区,程序再从缓冲区中读取rn字节的内容,此时缓冲区剩余bn = buf_size - rn字节。如果缓冲区不为空,rn < bn,则从缓冲区读取rn字节的内容,不发生文件IO。如果缓冲区不为空,rn >= bn,则从缓冲区读取bn字节的内容,不发生文件IO,缓冲区置为空,回归1/2步骤。缓冲读通过预读,可以在一定程度上减少文件IO次数,故提高性能。
代码演示:
package mainimport ( "bufio" "fmt" "strings")func main() { // 用 strings.Reader 模拟一个文件IO对象 strReader := strings.NewReader("12345678901234567890123456789012345678901234567890") // go 的缓冲区最小为 16 byte,我们用最小值比较容易演示 bufReader := bufio.NewReaderSize(strReader, 16) // bn = 0 但 rn >= buf_size 缓冲区不启用 发生文件IO tmpStr := make([]byte, 16) n, _ := bufReader.Read(tmpStr) // bufReader buffered: 0, content: 1234567890123456 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 0 rn < buf_size 缓冲区启用 // 缓冲区从文件读取 buf_size 字节 发生文件IO // 程序从缓冲区读取 rn 字节 // 缓冲区剩余 bn = buf_size - rn 字节 tmpStr = make([]byte, 15) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 1, content: 789012345678901 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 1 rn > bn // 程序从缓冲区读取 bn 字节 缓冲区置空 不发生文件IO // 注意这里只能读到一个字节 tmpStr = make([]byte, 10) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 0, content: 2 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 0 rn < buf_size 启用缓冲读 发生文件IO // 缓冲区从文件读取 buf_size 字节 // 程序从缓冲区读取 rn 字节 // 缓冲区剩余 bn = buf_size - rn 字节 tmpStr = make([]byte, 10) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 6, content: 3456789012 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 6 rn <= bn // 则程序冲缓冲区读取 rn 字节 不发生文件IO tmpStr = make([]byte, 3) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 3, content: 345 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 3 rn <= bn // 则程序冲缓冲区读取 rn 字节 不发生文件IO tmpStr = make([]byte, 3) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 0, content: 678 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n])}要注意的是当缓冲区中有内容时,程序的此次读取都会从缓冲区读,而不会发生文件IO。只有当缓冲区为空时,才会发生文件IO。如果缓冲区的大小足够,则启用缓冲读,先将内容载入填满缓冲区,程序再从缓冲区中读取。如果缓冲区过小,则会直接从文件读取,而不使用缓冲读。
...