理解底层— —Golang缓冲bufio,让io效率提升10倍(自实现)

Golang缓冲bufio,让效率提升10倍(自实现)

1 分析

我们都知道操作系统往内存写入数据的速度比往磁盘写快了好几个量级。主要原因是因为磁盘的IO耗时间过长。

  • 因此在通过代码操作文件时,我们要尽可能的减少磁盘的IO操作。
  • golang中提供了bufio这个包,为了更好的理解它的工作逻辑,我们底层自己来实现一个。

实现思路:

定义结构体

  • cache []byte:带cache缓冲
  • cacheIndex:用于标识当前缓冲区已经写入了多少数据
  • 讨论不同情况
    • ①写入的数据大小>=缓冲区长度:flush缓冲区,然后直接写磁盘
    • ②cache中已有的数据+要写入的数据 >= cache长度:先flush缓冲区数据到磁盘,再写入缓冲区
    • ③写入的数据长度 < 缓冲区:直接写入cache缓冲区

2 实战

2.1 定义结构体

//构造带缓冲的file结构体
type BufferedFileWriter struct {
	file       *os.File
	cache      []byte
	cacheIndex int
}

func NewBufferedFileWriter(fout *os.File, cacheSize int) *BufferedFileWriter {
	return &BufferedFileWriter{
		file: fout,
		//make([]byte,cacheSize):直接开辟了cacheSize大小的内存,并且赋默认值为0
		cache:      make([]byte, cacheSize),
		cacheIndex: 0,
	}
}

2.2 实现方法:WriteByte、WriteString、Flush

func (bf *BufferedFileWriter) WriteByte(data []byte) {
	if len(data) >= len(bf.cache) {
		//情况一:如果要写入的数据已经超过了cache本身长度:刷新cache,然后直接写入磁盘
		bf.Flush()
		bf.file.Write(data)
	} else {
		if bf.cacheIndex+len(data) >= len(bf.cache) {
			//情况二:cache中已有的数据+要写入的数据 >= cache长度,需要先刷新cache
			bf.Flush()
		}
		//情况三:要写入的数据 < cache长度
		copy(bf.cache[bf.cacheIndex:], data)
	}
}

func (bf *BufferedFileWriter) WriteString(data string) {
	bf.WriteByte([]byte(data))
}

func (bf *BufferedFileWriter) Flush() {
	bf.WriteByte(bf.cache[:bf.cacheIndex])
	bf.cacheIndex = 0
}

2.3 全部代码

package main

import (
	"fmt"
	"os"
	"time"
)

/*
	自定义一个bufio,感受golang中bufio的底层逻辑
*/
const log = "2023-08-20T09:20:45Z - INFO - User 789 accessed resource C"

//直接写文件
func WriteDirect(fileName string) {
	//如果文件不存在,则创建,如果文件已经有内容则覆盖,同时设置文件可读写;权限为0666
	file, err := os.OpenFile(fileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	for i := 0; i < 100000; i++ {
		file.WriteString(log)
	}
}

func main() {
	//测试直接写入文件的耗时:173ms
	//begin := time.Now()
	//WriteDirect("/Users/xsky/GolandProjects/MyTest/inter/a.txt")
	//fmt.Println("cost time : ", time.Since(begin).Milliseconds(), " ms")

	//测试带缓冲写入文件的耗时
	begin := time.Now()
	file, err := os.OpenFile("/Users/xsky/GolandProjects/MyTest/inter/b.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	bf := NewBufferedFileWriter(file, 1024)
	for i := 0; i < 100000; i++ {
		bf.WriteString(log)
	}
	//耗时5ms
	fmt.Println("cost time : ", time.Since(begin).Milliseconds(), " ms")
}

//构造带缓冲的file结构体
type BufferedFileWriter struct {
	file       *os.File
	cache      []byte
	cacheIndex int
}

func NewBufferedFileWriter(fout *os.File, cacheSize int) *BufferedFileWriter {
	return &BufferedFileWriter{
		file: fout,
		//make([]byte,cacheSize):直接开辟了cacheSize大小的内存,并且赋默认值为0
		cache:      make([]byte, cacheSize),
		cacheIndex: 0,
	}
}

func (bf *BufferedFileWriter) WriteByte(data []byte) {
	if len(data) >= len(bf.cache) {
		//情况一:如果要写入的数据已经超过了cache本身长度:刷新cache,然后直接写入磁盘
		bf.Flush()
		bf.file.Write(data)
	} else {
		if bf.cacheIndex+len(data) >= len(bf.cache) {
			//情况二:cache中已有的数据+要写入的数据 >= cache长度,需要先刷新cache
			bf.Flush()
		}
		//情况三:要写入的数据 < cache长度
		copy(bf.cache[bf.cacheIndex:], data)
	}
}

func (bf *BufferedFileWriter) WriteString(data string) {
	bf.WriteByte([]byte(data))
}

func (bf *BufferedFileWriter) Flush() {
	bf.WriteByte(bf.cache[:bf.cacheIndex])
	bf.cacheIndex = 0
}

3 测试结果

  • 不带缓冲区写入数据:173ms
  • 带缓冲区写入数据:5ms

你可能感兴趣的:(go,demo,golang,开发语言,后端,bufio,缓冲,底层代码)