用Golang对文件Hash

用Golang对文件Hash

曾几何时,题主曾用golang写过一个hash的工具,这次打算测试一下对文件的几种不同读取方式的比较。

  • 使用io/ioutl.ReadAll
  • 使用io.Copy
  • 使用bufio.Reader+io.Copy

当前目录下有个20M左右的文件poe.tgz,新建了main_test.go文件,这次使用SHA1测试。

~ ls -l ./poe.tgz
-rw-------  1 ... ...  20726272 12  7 18:22 ./poe.tgz
~ shasum -a 1 ./poe.tgz
0bd099caa5ac2be51075085daea9cbac89d8a6be  ./poe.tgz

上代码:

package main

import (
    "bufio"
    "crypto/sha1"
    "fmt"
    "io"
    "io/ioutil"
    "os"
    "runtime"
    "testing"
)

const (
    fname    = "./poe.tgz"
    hashSum  = "0bd099caa5ac2be51075085daea9cbac89d8a6be"
)

func copyFile() {
    f, err := os.Open(fname)
    if err != nil {
        panic(err)
        return
    }
    defer f.Close()

    h := sha1.New()
    _, err = io.Copy(h, f)

    if err != nil {
        panic(err)
        return
    }
    if fmt.Sprintf("%x", h.Sum(nil)) != hashSum {
        panic(fmt.Errorf("not equal"))
    }
}

func readAll() {
    runtime.GC()
    f, err := os.Open(fname)
    if err != nil {
        panic(err)
        return
    }
    defer f.Close()

    h := sha1.New()
    bs, err := ioutil.ReadAll(f)
    if err != nil {
        panic(err)
        return
    }

    _, err = h.Write(bs)
    if err != nil {
        panic(err)
        return
    }
    if fmt.Sprintf("%x", h.Sum(nil)) != hashSum {
        panic(fmt.Errorf("not equal"))
    }
}

func bufioRead() {
    f, err := os.Open(fname)
    if err != nil {
        panic(err)
        return
    }
    defer f.Close()

    br := bufio.NewReader(f)

    h := sha1.New()
    _, err = io.Copy(h, br)

    if err != nil {
        panic(err)
        return
    }
    if fmt.Sprintf("%x", h.Sum(nil)) != hashSum {
        panic(fmt.Errorf("not equal"))
    }
}

func BenchmarkCopy(b *testing.B) {
    for i := 0; i < b.N; i++ {
        copyFile()
    }
}

func BenchmarkBufio(b *testing.B) {
    for i := 0; i < b.N; i++ {
        bufioRead()
    }
}

func BenchmarkReadall(b *testing.B) {
    for i := 0; i < b.N; i++ {
        readAll()
    }
}

~ go test -bench=".*" -benchmem  main_test.go
testing: warning: no tests to run
BenchmarkCopy-4               30      34397326 ns/op       33087 B/op          8 allocs/op
BenchmarkBufio-4              50      37697430 ns/op        4492 B/op          9 allocs/op
BenchmarkReadall-4            30      47578226 ns/op    67108143 B/op         30 allocs/op
PASS
ok      command-line-arguments  5.480s

结果发现,无论是效率还是内存,bufio.Reader+io.Copy都是最优的方式,其次是io.Copy,而io/ioutl.ReadAll读取了全部文件到内存。

后又换了个0.5K的小文件进行测试,发现差距就比较明显了

~ go test -bench=".*" -benchmem  main_test.go
testing: warning: no tests to run
BenchmarkCopy-4           100000         14969 ns/op       33057 B/op          8 allocs/op
BenchmarkBufio-4          200000          8618 ns/op        4473 B/op          9 allocs/op
BenchmarkReadall-4         10000        240270 ns/op        3360 B/op         16 allocs/op
PASS
ok      command-line-arguments  5.882s

而且还可以看出,无论数据量大小,io.Copybufio.Reader始终都使用了32K4K的缓冲区,当然看源码里面更清楚。

—END—

Spread the love
Comments are closed.