golang读文件分析2

2022-04-25 09:02:57 浏览数 (2)

上一个文件我们对go读文件的方式有所了解,这个我们深入一下读文件,我们如何对大文件进行读取呢?我们需要从耗时,性能等方面来考虑。

首先我们先看下读10m文件和1.3g的文件,用readAll的耗时和内存的使用。

一个16m的文件

-rw-r--r-- 1 13727 197609 12M Mar 13 18:41 stat.log.2021-03-10-16

一个1.3g的文件

-rw-r--r-- 1 13727 197609 1.3G Mar 14 21:10 stat.log.2021-03-10-167

运行如下代码执行

代码语言:javascript复制
package main

import (
  "fmt"
  "io/ioutil"
  "os"
  "runtime"
  "time"
)

func main() {
  //读文件
  file, err := os.Open("E://stat.log.2021-03-10-16")
  if err != nil {
    fmt.Println("读文件失败", err)
    return
  }
  defer file.Close()
  t1 := time.Now().UnixNano()
  content, err := ioutil.ReadAll(file)
  if err != nil {
    fmt.Println("读内容失败", err)
    return
  }
  fmt.Println(len(string(content)))
  t2 := time.Now().UnixNano()
  fmt.Println((float64(t2) - float64(t1)) / 1000000000)
  traceMemStats()
}

func traceMemStats() {
  var ms runtime.MemStats
  runtime.ReadMemStats(&ms)
  fmt.Printf("TotalAlloc:%d(m) Alloc:%d(m)", ms.TotalAlloc/1024/1024, ms.Alloc/1024/1024)
}

16m文件执行结果如下:

代码语言:javascript复制
字节长度:11588920
0.02125312
TotalAlloc:43(m) Alloc:35(m)

1.3g的文件执行如下:

代码语言:javascript复制
字节长度:1321162080
2.37205248
TotalAlloc:5356(m) Alloc:4332(m)

上面的结果很显然,耗时比较长,而且内存占用高。针对于这样的情况,我们可以使用sync.pool,使用bufio.NewReader来实现读取。

我们sync.pool主要是复用内存,让内存不占用那么高,sync.pool之前我有文章讲过,这个很适合读文件的这个功能。

我们先看下代码:

代码语言:javascript复制
package main

import (
  "bufio"
  "fmt"
  "io"
  "os"
  "runtime"
  "sync"
  "time"
)

func main() {
  //读文件
  file, err := os.Open("E://stat.log.2021-03-10-16")
  if err != nil {
    fmt.Println("读文件失败", err)
    return
  }
  t1 := time.Now().UnixNano()
  linesPool := sync.Pool{New: func() interface{} {
    lines := make([]byte, 12*2048)
    return lines
  }}
  var r *bufio.Reader
  r = bufio.NewReader(file)
  content := []byte{}
  for {
    buf := linesPool.Get().([]byte)
    n, err := r.Read(buf)
    buf = buf[:n]
    if n == 0 {
      if err == io.EOF {
        break
      }
      if err != nil {
        break
      }
      return
    }
    content = append(content, buf...)
  }
  fmt.Println("字节长度:", len(string(content)))
  t2 := time.Now().UnixNano()
  fmt.Println((float64(t2) - float64(t1)) / 1000000000)
  traceMemStats1()
}

func traceMemStats1() {
  var ms runtime.MemStats
  runtime.ReadMemStats(&ms)
  fmt.Printf("Alloc:%d(m) HeapIdle:%d(m) HeapReleased:%d(m)", ms.Alloc/1024/1024, ms.HeapIdle/1024/1024, ms.HeapReleased/1024/1024)
}

16m文件执行结果如下:随着我们设置sync.pool的大小,内存占用会不一样,sync.pool设置越大,堆内存占用的就越少。

代码语言:javascript复制
字节长度:11588920
0.181612032
TotalAlloc:89(m) Alloc:24(m)

1.3g的文件执行如下:sync.pool的大小设置,对内存占用影响比较大,gc回收的速度也会受影响。

代码语言:javascript复制
字节长度:1321162080
4.201924352
TotalAlloc:9893(m) Alloc:2815(m)

单从耗时上面看ReadAll是比较好的选择,但是ReadAll有一个弊端,如果读超大文件,内存不容易被释放,这样会造成内存占用比较久,bufio读文件占用总内存比较多,整体来说使用内存是比较少的,如果线上用不容易出问题,耗时也还是比较乐观的。

为什么会有如此的差异呢?下次我们从原理上面来分析。

0 人点赞