Skip to content

Commit

Permalink
merkle: optimize hashing by reusing sha256 in a pool
Browse files Browse the repository at this point in the history
This change optimizes SHA256 hashing for leafHash & innerHash
by pooling and reusing hashes, which massively reduces on RAM
consumption and even CPU time (benchmarking on my noisy machine
is skewed but the results can be verified individually) producing
the results below:

```shell
$ benchstat before.txt after.txt
name                          old time/op    new time/op    delta
HashAlternatives/recursive-8    77.6µs ± 2%    77.0µs ± 2%     ~     (p=0.165 n=10+10)
HashAlternatives/iterative-8    76.3µs ± 1%    76.2µs ± 3%     ~     (p=0.720 n=9+10)

name                          old alloc/op   new alloc/op   delta
HashAlternatives/recursive-8    25.4kB ± 0%     6.4kB ± 0%  -74.94%  (p=0.000 n=10+10)
HashAlternatives/iterative-8    28.1kB ± 0%     9.1kB ± 0%  -67.78%  (p=0.000 n=10+10)

name                          old allocs/op  new allocs/op  delta
HashAlternatives/recursive-8       497 ± 0%       199 ± 0%  -59.96%  (p=0.000 n=10+10)
HashAlternatives/iterative-8       498 ± 0%       200 ± 0%  -59.84%  (p=0.000 n=10+10)
```

Fixes #43
  • Loading branch information
odeke-em committed Feb 29, 2024
1 parent c20eda9 commit af9405a
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 6 deletions.
25 changes: 19 additions & 6 deletions merkle/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package merkle

import (
"crypto/sha256"
shash "hash"
"sync"
)

// TODO: make these have a large predefined capacity
Expand All @@ -17,15 +19,26 @@ func emptyHash() []byte {

// returns sha256(0x00 || leaf)
func leafHash(leaf []byte) []byte {
return hash(append(leafPrefix, leaf...))
return hash(leafPrefix, leaf)
}

// returns sha256(0x01 || left || right)
func innerHash(left []byte, right []byte) []byte {
return hash(append(innerPrefix, append(left, right...)...))
func innerHash(left, right []byte) []byte {
return hash(innerPrefix, left, right)
}

func hash(bz []byte) []byte {
h := sha256.Sum256(bz)
return h[:]
var sha256Pool = &sync.Pool{New: func() any { return sha256.New() }}

func hash(slices ...[]byte) []byte {
h := sha256Pool.Get().(shash.Hash)
defer func() {
h.Reset()
sha256Pool.Put(h)
}()

for _, slice := range slices {
h.Write(slice)
}

return h.Sum(nil)
}
2 changes: 2 additions & 0 deletions merkle/tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,14 @@ func BenchmarkHashAlternatives(b *testing.B) {

b.ResetTimer()
b.Run("recursive", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = HashFromByteSlices(items)
}
})

b.Run("iterative", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = HashFromByteSlicesIterative(items)
}
Expand Down

0 comments on commit af9405a

Please sign in to comment.