Created
September 4, 2019 10:18
-
-
Save jpeletier/a7e36762f9b4f89e10247f8f8c0151c6 to your computer and use it in GitHub Desktop.
Hasher pooling benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package experiments | |
import ( | |
"encoding/binary" | |
"fmt" | |
"math/rand" | |
"strconv" | |
"strings" | |
"sync" | |
"testing" | |
"github.com/ethereum/go-ethereum/log" | |
"github.com/ethersphere/swarm/bmt" | |
"github.com/ethersphere/swarm/storage" | |
) | |
func init() { | |
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StderrHandler)) | |
} | |
type hasher interface { | |
hash([]byte) []byte | |
} | |
type simple struct { | |
mu sync.Mutex | |
hasher storage.SwarmHash | |
} | |
func newSimple(hasherFactory storage.SwarmHasher) *simple { | |
return &simple{ | |
hasher: hasherFactory(), | |
} | |
} | |
func (s *simple) hash(data []byte) []byte { | |
s.mu.Lock() | |
defer s.mu.Unlock() | |
return doHash(s.hasher, data) | |
} | |
type instancer struct { | |
hashFunc storage.SwarmHasher | |
} | |
func newInstancer(hasherFactory storage.SwarmHasher) *instancer { | |
return &instancer{ | |
hashFunc: hasherFactory, | |
} | |
} | |
func (i *instancer) hash(data []byte) []byte { | |
hasher := i.hashFunc() | |
resetLength := make([]byte, 8) | |
binary.LittleEndian.PutUint64(resetLength, uint64(len(data))) | |
_, ok := hasher.(*bmt.Hasher) | |
if ok { | |
hasher.ResetWithLength(resetLength) | |
} else { | |
hasher.Write(resetLength) | |
} | |
hasher.Write(data) | |
return hasher.Sum(nil) | |
} | |
type pool struct { | |
workers sync.Pool | |
inc int | |
} | |
func newPool(hasherFactory storage.SwarmHasher, workerCount int64) *pool { | |
p := &pool{} | |
p.workers = sync.Pool{ | |
New: func() interface{} { | |
p.inc++ | |
return hasherFactory() | |
}, | |
} | |
for i := int64(0); i < workerCount; i++ { | |
p.workers.Put(hasherFactory()) | |
} | |
return p | |
} | |
func (p *pool) hash(data []byte) []byte { | |
h := p.workers.Get().(storage.SwarmHash) | |
defer p.workers.Put(h) | |
return doHash(h, data) | |
} | |
func doHash(h storage.SwarmHash, data []byte) []byte { | |
resetLength := make([]byte, 8) | |
binary.LittleEndian.PutUint64(resetLength, uint64(len(data))) | |
h.ResetWithLength(resetLength) | |
h.Write(data) | |
return h.Sum(nil) | |
} | |
func BenchmarkPool(b *testing.B) { | |
methods := []string{"simple", "pool", "instance"} | |
algos := []string{"BMT", "SHA3", "SHA256"} | |
runBenchmark := func(workers, passes, size int) { | |
for _, algo := range algos { | |
for _, method := range methods { | |
b.Run(fmt.Sprintf("%s/%d/%d/%d/%s", method, workers, passes, size, algo), benchmarkPool) | |
} | |
} | |
} | |
runBenchmark(100, 100, 256) | |
runBenchmark(100, 1000, 256) | |
runBenchmark(100, 100, 4096) | |
runBenchmark(100, 1000, 4096) | |
runBenchmark(1000, 1000, 256) | |
runBenchmark(1000, 10000, 256) | |
runBenchmark(1000, 1000, 4096) | |
runBenchmark(1000, 10000, 4096) | |
runBenchmark(100, 10000, 256) | |
runBenchmark(100, 10000, 4096) | |
} | |
func benchmarkPool(b *testing.B) { | |
params := strings.Split(b.Name(), "/") | |
mode := params[1] | |
workers, _ := strconv.ParseInt(params[2], 10, 0) | |
passes, _ := strconv.ParseInt(params[3], 10, 0) | |
size, _ := strconv.ParseInt(params[4], 10, 0) | |
algo := params[5] | |
data := make([]byte, passes+size) | |
c, err := rand.Read(data) | |
if int64(c) != passes+size { | |
b.Fatal("short read") | |
} | |
if err != nil { | |
b.Fatal(err) | |
} | |
swarmHasher := storage.MakeHashFunc(algo) | |
var hasherUsed hasher | |
switch mode { | |
case "simple": | |
hasherUsed = newSimple(swarmHasher) | |
case "pool": | |
hasherUsed = newPool(swarmHasher, workers) | |
default: | |
hasherUsed = newInstancer(swarmHasher) | |
} | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
wg := sync.WaitGroup{} | |
for j := int64(0); j < passes; j++ { | |
wg.Add(1) | |
go func(j int64) { | |
hasherUsed.hash(data[j : j+size]) | |
wg.Done() | |
}(j) | |
} | |
wg.Wait() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment