golang读取文件的方式主要有4种:golang
关于前3种方式的速度比较,我最先是在 GoLang几种读文件方式的比较 看过,但在该blog的评论区有人(study_c)提出了质疑,并提供了测试代码。根据该代码的测试,结果应该是数组
bufio > ioutil.ReadAll > File自带Read缓存
在我反复跑study_c测试代码过程当中发现几个问题或者说是影响因素:dom
因此本文的性能测试就是基于study_c的代码的基础上作了修改,尝试测试不一样块大小对结果的影响,并增长对ioutil.ReadFile()的测试,还有随机生成文件以应对缓存影响公平性。函数
测试环境性能
CPU: i5-6300HQ
MEM: 12GB
DSK: SANDISK Extreme PRO SSD 480GB
OS : WIN 10 64bit测试
测试代码1【randfiles.go】,生成1-500MB包含随机字符串的文件spa
package main import ( "math/rand" "fmt" "flag" "strconv" "io/ioutil" ) const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang func RandStringBytes(n int) []byte { b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Intn(len(letterBytes))] } return b } func RandFile(path string,filesizeMB int) { b:=RandStringBytes(filesizeMB * 1024) //生成1-500KB大小的随机字符串 bb := make([]byte, filesizeMB * 1024 * 1024) for i:=0;i<1024;i++ { //复制1024遍 copy(bb[len(b)*i:len(b)*(i+1)],b) } //fmt.Printf("%s",b) ioutil.WriteFile(path,bb,0666) } func main() { flag.Parse() filesizeMB,err :=strconv.Atoi(flag.Arg(0)) //1-500MB大小的文件 if err != nil{panic(err)} if filesizeMB > 500 {panic("too large file,>500MB")} RandFile("./random1.txt",filesizeMB) RandFile("./random2.txt",filesizeMB) RandFile("./random3.txt",filesizeMB) RandFile("./random4.txt",filesizeMB) fmt.Printf("Created 4 files, each file size is %d MB.",filesizeMB) }
测试代码2【speed.go】,性能测试code
package main import( "fmt" "os" "flag" "io" "io/ioutil" "bufio" "time" "strconv" ) func read1(path string,blocksize int){ fi,err := os.Open(path) if err != nil{ panic(err) } defer fi.Close() block := make([]byte,blocksize) for{ n,err := fi.Read(block) if err != nil && err != io.EOF{panic(err)} if 0 ==n {break} } } func read2(path string,blocksize int){ fi,err := os.Open(path) if err != nil{panic(err)} defer fi.Close() r := bufio.NewReader(fi) block := make([]byte,blocksize) for{ n,err := r.Read(block) if err != nil && err != io.EOF{panic(err)} if 0 ==n {break} } } func read3(path string){ fi,err := os.Open(path) if err != nil{panic(err)} defer fi.Close() _,err = ioutil.ReadAll(fi) } func read4(path string){ _,err := ioutil.ReadFile(path) if err != nil{panic(err)} } func main(){ flag.Parse() file1 := "./random1.txt" file2 := "./random2.txt" file3 := "./random3.txt" file4 := "./random4.txt" blocksize,_ :=strconv.Atoi(flag.Arg(0)) var start,end time.Time start = time.Now() read1(file1,blocksize) end = time.Now() fmt.Printf("file/Read() cost time %v\n",end.Sub(start)) start = time.Now() read2(file2,blocksize) end = time.Now() fmt.Printf("bufio/Read() cost time %v\n",end.Sub(start)) start = time.Now() read3(file3) end = time.Now() fmt.Printf("ioutil.ReadAll() cost time %v\n",end.Sub(start)) start = time.Now() read4(file4) end = time.Now() fmt.Printf("ioutil.ReadFile() cost time %v\n",end.Sub(start)) }
测试结果:
测试1:块大小为4KB,这是个常见的大小,出人意料ioutil.ReadAll()最慢
测试2:块大小为1KB,这是前言提到的测试结果所用的块大小,与其测试结果一致
测试3:块大小为32KB,在大块的状况下,调用Read()次数更少,bufio已经没有优点,但前二者却远快于ioutil包的两个函数
测试4:块大小为16字节,在小块的状况下,没有缓存的文件普通Read成绩惨不忍睹blog
在查阅golang标准库的源代码后,之因此有不一样的结果是与每一个方法的实现相关的,最大的因素就是内部buffer的大小,这个直接决定了读取的快慢: