[crypto] Optimize the FromRLP with sync pool, and provide benchmarks to approve the performance improvement

pull/3616/head
Jacky Wang 4 years ago
parent c4a9b77bee
commit a67b7b028b
No known key found for this signature in database
GPG Key ID: 1085CE5F4FF5842C
  1. 29
      crypto/hash/rlp.go
  2. 77
      crypto/hash/rlp_test.go

@ -1,22 +1,47 @@
package hash
import (
"hash"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
)
var kec256Pool = sync.Pool{
New: func() interface{} {
return sha3.NewLegacyKeccak256()
},
}
// FromRLP hashes the RLP representation of the given object.
func FromRLP(x interface{}) (h common.Hash) {
hw := sha3.NewLegacyKeccak256()
hw := kec256Pool.Get().(hash.Hash)
defer func() {
hw.Reset()
kec256Pool.Put(hw)
}()
rlp.Encode(hw, x)
hw.Sum(h[:0])
return h
}
var sha256Pool = sync.Pool{
New: func() interface{} {
return sha3.New256()
},
}
// FromRLPNew256 hashes the RLP representation of the given object using New256
func FromRLPNew256(x interface{}) (h common.Hash) {
hw := sha3.New256()
hw := sha256Pool.Get().(hash.Hash)
defer func() {
hw.Reset()
sha256Pool.Put(hw)
}()
rlp.Encode(hw, x)
hw.Sum(h[:0])
return h

@ -0,0 +1,77 @@
package hash
import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
)
// This test file test the bench mark of running `FromRLP` with or without usage of sync.Pool
// in single-threaded / multi-threaded case.
//
// Here is the test results:
//
// > go test -bench=. -benchmem
// goos: darwin
// goarch: amd64
// pkg: github.com/harmony-one/harmony/crypto/hash
// BenchmarkFromRLP_MultiThreaded-12 4841481 263 ns/op 560 B/op 5 allocs/op
// BenchmarkFromRLPNoPool_MultiThreaded-12 3665227 344 ns/op 1008 B/op 6 allocs/op
// BenchmarkFromRLP_SingleThreaded-12 1303647 909 ns/op 560 B/op 5 allocs/op
// BenchmarkFromRLPNoPool_SingleThreaded-12 1296238 920 ns/op 1008 B/op 6 allocs/op
// PASS
// ok github.com/harmony-one/harmony/crypto/hash 7.441s
//
// We can see that FromRLP with sync pool implementation has better memory and CPU performance over
// the implementation without sync pool. This is because the sync pool implementation will reuse
// the hasher and results in better performance both in CPU and memory. The huge CPU difference
// in multi-threading benchmark actually results from sync.Pool's optimization over golang's GC
// mechanism.
func BenchmarkFromRLP_MultiThreaded(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
FromRLP(testObj)
}
})
}
func BenchmarkFromRLPNoPool_MultiThreaded(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
fromRLPNoPool(testObj)
}
})
}
func BenchmarkFromRLP_SingleThreaded(b *testing.B) {
for i := 0; i != b.N; i++ {
FromRLP(testObj)
}
}
func BenchmarkFromRLPNoPool_SingleThreaded(b *testing.B) {
for i := 0; i != b.N; i++ {
fromRLPNoPool(testObj)
}
}
// fromRLPNoPool is a compared function to showcase the FromRLP function with sync pool
func fromRLPNoPool(x interface{}) (h common.Hash) {
hw := sha3.NewLegacyKeccak256()
rlp.Encode(hw, x)
hw.Sum(h[:0])
return h
}
var testObj = testStruct{
field1: "harmony",
field2: 1024,
}
type testStruct struct {
field1 string
field2 int
}
Loading…
Cancel
Save