| package xxhashbench |
| |
| import ( |
| "fmt" |
| "hash/crc32" |
| "hash/fnv" |
| "testing" |
| |
| OneOfOne "github.com/OneOfOne/xxhash" |
| "github.com/cespare/xxhash/v2" |
| "github.com/spaolacci/murmur3" |
| ) |
| |
| // TODO: The main benchmarks live in the xxhash package now, so the only purpose |
| // of this is to compare different hash functions. Consider deleting xxhashbench |
| // or replacing it with a more minimal comparison. |
| |
| var sink uint64 |
| |
| var benchmarks = []struct { |
| name string |
| directBytes func([]byte) uint64 |
| directString func(string) uint64 |
| digestBytes func([]byte) uint64 |
| digestString func(string) uint64 |
| }{ |
| { |
| name: "xxhash", |
| directBytes: xxhash.Sum64, |
| directString: xxhash.Sum64String, |
| digestBytes: func(b []byte) uint64 { |
| h := xxhash.New() |
| h.Write(b) |
| return h.Sum64() |
| }, |
| digestString: func(s string) uint64 { |
| h := xxhash.New() |
| h.WriteString(s) |
| return h.Sum64() |
| }, |
| }, |
| { |
| name: "OneOfOne", |
| directBytes: OneOfOne.Checksum64, |
| directString: OneOfOne.ChecksumString64, |
| digestBytes: func(b []byte) uint64 { |
| h := OneOfOne.New64() |
| h.Write(b) |
| return h.Sum64() |
| }, |
| digestString: func(s string) uint64 { |
| h := OneOfOne.New64() |
| h.WriteString(s) |
| return h.Sum64() |
| }, |
| }, |
| { |
| name: "murmur3", |
| directBytes: murmur3.Sum64, |
| directString: func(s string) uint64 { |
| return murmur3.Sum64([]byte(s)) |
| }, |
| digestBytes: func(b []byte) uint64 { |
| h := murmur3.New64() |
| h.Write(b) |
| return h.Sum64() |
| }, |
| digestString: func(s string) uint64 { |
| h := murmur3.New64() |
| h.Write([]byte(s)) |
| return h.Sum64() |
| }, |
| }, |
| { |
| name: "CRC-32", |
| directBytes: func(b []byte) uint64 { |
| return uint64(crc32.ChecksumIEEE(b)) |
| }, |
| directString: func(s string) uint64 { |
| return uint64(crc32.ChecksumIEEE([]byte(s))) |
| }, |
| digestBytes: func(b []byte) uint64 { |
| h := crc32.NewIEEE() |
| h.Write(b) |
| return uint64(h.Sum32()) |
| }, |
| digestString: func(s string) uint64 { |
| h := crc32.NewIEEE() |
| h.Write([]byte(s)) |
| return uint64(h.Sum32()) |
| }, |
| }, |
| { |
| name: "FNV-1a", |
| digestBytes: func(b []byte) uint64 { |
| h := fnv.New64() |
| h.Write(b) |
| return h.Sum64() |
| }, |
| digestString: func(s string) uint64 { |
| h := fnv.New64a() |
| h.Write([]byte(s)) |
| return h.Sum64() |
| }, |
| }, |
| } |
| |
| func BenchmarkHashes(b *testing.B) { |
| for _, bb := range benchmarks { |
| for _, benchSize := range []struct { |
| name string |
| n int |
| }{ |
| {"5B", 5}, |
| {"100B", 100}, |
| {"4KB", 4e3}, |
| {"10MB", 10e6}, |
| } { |
| input := make([]byte, benchSize.n) |
| for i := range input { |
| input[i] = byte(i) |
| } |
| inputString := string(input) |
| if bb.directBytes != nil { |
| name := fmt.Sprintf("%s,direct,bytes,n=%s", bb.name, benchSize.name) |
| b.Run(name, func(b *testing.B) { |
| benchmarkHashBytes(b, input, bb.directBytes) |
| }) |
| } |
| if bb.directString != nil { |
| name := fmt.Sprintf("%s,direct,string,n=%s", bb.name, benchSize.name) |
| b.Run(name, func(b *testing.B) { |
| benchmarkHashString(b, inputString, bb.directString) |
| }) |
| } |
| if bb.digestBytes != nil { |
| name := fmt.Sprintf("%s,digest,bytes,n=%s", bb.name, benchSize.name) |
| b.Run(name, func(b *testing.B) { |
| benchmarkHashBytes(b, input, bb.digestBytes) |
| }) |
| } |
| if bb.digestString != nil { |
| name := fmt.Sprintf("%s,digest,string,n=%s", bb.name, benchSize.name) |
| b.Run(name, func(b *testing.B) { |
| benchmarkHashString(b, inputString, bb.digestString) |
| }) |
| } |
| } |
| } |
| } |
| |
| func benchmarkHashBytes(b *testing.B, input []byte, fn func([]byte) uint64) { |
| b.SetBytes(int64(len(input))) |
| for i := 0; i < b.N; i++ { |
| sink = fn(input) |
| } |
| } |
| |
| func benchmarkHashString(b *testing.B, input string, fn func(string) uint64) { |
| b.SetBytes(int64(len(input))) |
| for i := 0; i < b.N; i++ { |
| sink = fn(input) |
| } |
| } |