mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-23 19:31:18 +00:00
hd: optimize ReverseBytes + add tests
* Optimized ReverseBytes to: a) Minimally allocate --> 60.0% reduction in the number of allocations b) Only walk halfway the length of the string thus performing byte swaps from left to right. Improves the performance as well. Complexity is O(n/2) instead of O(n) which is still O(n) but benchmarks show the new time is in deed 1/2 of the original time. * Added unit tests and some common cases to ensure correctness. * Benchmark shoot out results: ```shell name old time/op new time/op delta ReverseBytes-4 554ns ± 4% 242ns ± 3% -56.20% (p=0.000 n=10+10) name old alloc/op new alloc/op delta ReverseBytes-4 208B ± 0% 114B ± 0% -45.19% (p=0.000 n=10+10) name old allocs/op new allocs/op delta ReverseBytes-4 10.0 ± 0% 4.0 ± 0% -60.00% (p=0.000 n=10+10) ```
This commit is contained in:
parent
bf355d1b58
commit
ae9c5b1ca0
@ -282,9 +282,20 @@ func CalcSha512(buf []byte) []byte {
|
||||
}
|
||||
|
||||
func ReverseBytes(buf []byte) []byte {
|
||||
res := []byte{}
|
||||
for i := len(buf) - 1; i >= 0; i-- {
|
||||
res = append(res, buf[i])
|
||||
var res []byte
|
||||
if len(buf) == 0 {
|
||||
return res
|
||||
}
|
||||
|
||||
// Walk till mid-way, swapping bytes from each end:
|
||||
// b[i] and b[len-i-1]
|
||||
blen := len(buf)
|
||||
res = make([]byte, blen)
|
||||
mid := blen / 2
|
||||
for left := 0; left <= mid; left++ {
|
||||
right := blen - left - 1
|
||||
res[left] = buf[right]
|
||||
res[right] = buf[left]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
@ -89,6 +89,26 @@ func TestHDToAddr(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseBytes(t *testing.T) {
|
||||
tests := [...]struct {
|
||||
v []byte
|
||||
want []byte
|
||||
}{
|
||||
{[]byte(""), []byte("")},
|
||||
{nil, nil},
|
||||
{[]byte("Tendermint"), []byte("tnimredneT")},
|
||||
{[]byte("T"), []byte("T")},
|
||||
{[]byte("Te"), []byte("eT")},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got := ReverseBytes(tt.v)
|
||||
if !bytes.Equal(got, tt.want) {
|
||||
t.Errorf("#%d:\ngot= (%x)\nwant=(%x)", i, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ifExit(err error, n int) {
|
||||
if err != nil {
|
||||
fmt.Println(n, err)
|
||||
@ -187,3 +207,36 @@ func tylerSmith(seed []byte) ([]byte, []byte, []byte) {
|
||||
pub := k.PublicKey().Key
|
||||
return masterKey.Key, priv, pub
|
||||
}
|
||||
|
||||
// Benchmarks
|
||||
|
||||
var revBytesCases = [][]byte{
|
||||
nil,
|
||||
[]byte(""),
|
||||
|
||||
[]byte("12"),
|
||||
|
||||
// 16byte case
|
||||
[]byte("abcdefghijklmnop"),
|
||||
|
||||
// 32byte case
|
||||
[]byte("abcdefghijklmnopqrstuvwxyz123456"),
|
||||
|
||||
// 64byte case
|
||||
[]byte("abcdefghijklmnopqrstuvwxyz123456abcdefghijklmnopqrstuvwxyz123456"),
|
||||
}
|
||||
|
||||
func BenchmarkReverseBytes(b *testing.B) {
|
||||
var sink []byte
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, tt := range revBytesCases {
|
||||
sink = ReverseBytes(tt)
|
||||
}
|
||||
}
|
||||
b.ReportAllocs()
|
||||
|
||||
// sink is necessary to ensure if the compiler tries
|
||||
// to smart, that it won't optimize away the benchmarks.
|
||||
if sink != nil {
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user