[#1444] pilorama: Optimize internal encoding/decoding

```
name                      old time/op    new time/op    delta
ApplySequential/bbolt-8     55.5µs ± 4%    55.5µs ± 3%     ~     (p=1.000 n=10+7)
ApplyReorderLast/bbolt-8     108µs ± 6%     112µs ± 8%     ~     (p=0.077 n=9+9)

name                      old alloc/op   new alloc/op   delta
ApplySequential/bbolt-8     28.8kB ± 3%    27.7kB ± 6%   -3.79%  (p=0.005 n=10+10)
ApplyReorderLast/bbolt-8    41.4kB ± 5%    38.9kB ± 5%   -6.19%  (p=0.001 n=10+9)

name                      old allocs/op  new allocs/op  delta
ApplySequential/bbolt-8        262 ± 2%       235 ±10%  -10.41%  (p=0.000 n=10+10)
ApplyReorderLast/bbolt-8       684 ± 6%       616 ± 7%  -10.04%  (p=0.000 n=10+9)
```

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2022-05-28 12:39:37 +03:00 committed by fyrchik
parent b04f712773
commit 8027b7bb6b
2 changed files with 78 additions and 38 deletions

View file

@ -231,9 +231,13 @@ func (t *boltForest) applyOperation(logBucket, treeBucket *bbolt.Bucket, m *Move
key, value := c.Last() key, value := c.Last()
b := bytes.NewReader(nil)
r := io.NewBinReaderFromIO(b)
// 1. Undo up until the desired timestamp is here. // 1. Undo up until the desired timestamp is here.
for len(key) == 8 && binary.BigEndian.Uint64(key) > m.Time { for len(key) == 8 && binary.BigEndian.Uint64(key) > m.Time {
if err := t.logFromBytes(&tmp, value); err != nil { b.Reset(value)
if err := t.logFromBytes(&tmp, r); err != nil {
return nil, err return nil, err
} }
if err := t.undo(&tmp.Move, &tmp, treeBucket, cKey[:]); err != nil { if err := t.undo(&tmp.Move, &tmp, treeBucket, cKey[:]); err != nil {
@ -253,7 +257,8 @@ func (t *boltForest) applyOperation(logBucket, treeBucket *bbolt.Bucket, m *Move
// 3. Re-apply all other operations. // 3. Re-apply all other operations.
for len(key) == 8 { for len(key) == 8 {
if err := t.logFromBytes(&tmp, value); err != nil { b.Reset(value)
if err := t.logFromBytes(&tmp, r); err != nil {
return nil, err return nil, err
} }
if err := t.do(logBucket, treeBucket, cKey[:], &tmp); err != nil { if err := t.do(logBucket, treeBucket, cKey[:], &tmp); err != nil {
@ -559,40 +564,37 @@ func (t *boltForest) moveFromBytes(m *Move, data []byte) error {
r := io.NewBinReaderFromBuf(data) r := io.NewBinReaderFromBuf(data)
m.Child = r.ReadU64LE() m.Child = r.ReadU64LE()
m.Parent = r.ReadU64LE() m.Parent = r.ReadU64LE()
if err := m.Meta.FromBytes(r.ReadVarBytes()); err != nil { m.Meta.DecodeBinary(r)
return err
}
return r.Err return r.Err
} }
func (t *boltForest) logFromBytes(lm *LogMove, data []byte) error { func (t *boltForest) logFromBytes(lm *LogMove, r *io.BinReader) error {
r := io.NewBinReaderFromBuf(data)
lm.Child = r.ReadU64LE() lm.Child = r.ReadU64LE()
lm.Parent = r.ReadU64LE() lm.Parent = r.ReadU64LE()
if err := lm.Meta.FromBytes(r.ReadVarBytes()); err != nil { lm.Meta.DecodeBinary(r)
return err
}
lm.HasOld = r.ReadBool() lm.HasOld = r.ReadBool()
if lm.HasOld { if lm.HasOld {
lm.Old.Parent = r.ReadU64LE() lm.Old.Parent = r.ReadU64LE()
if err := lm.Old.Meta.FromBytes(r.ReadVarBytes()); err != nil { lm.Old.Meta.DecodeBinary(r)
return err
}
} }
return r.Err return r.Err
} }
func (t *boltForest) logToBytes(lm *LogMove) []byte { func (t *boltForest) logToBytes(lm *LogMove) []byte {
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
size := 8 + 8 + lm.Meta.Size() + 1
if lm.HasOld {
size += 8 + lm.Old.Meta.Size()
}
w.Grow(size)
w.WriteU64LE(lm.Child) w.WriteU64LE(lm.Child)
w.WriteU64LE(lm.Parent) w.WriteU64LE(lm.Parent)
w.WriteVarBytes(lm.Meta.Bytes()) lm.Meta.EncodeBinary(w.BinWriter)
w.WriteBool(lm.HasOld) w.WriteBool(lm.HasOld)
if lm.HasOld { if lm.HasOld {
w.WriteU64LE(lm.Old.Parent) w.WriteU64LE(lm.Old.Parent)
w.WriteVarBytes(lm.Old.Meta.Bytes()) lm.Old.Meta.EncodeBinary(w.BinWriter)
} }
return w.Bytes() return w.Bytes()
} }

View file

@ -10,31 +10,13 @@ func (x *Meta) FromBytes(data []byte) error {
} }
r := io.NewBinReaderFromBuf(data) r := io.NewBinReaderFromBuf(data)
ts := r.ReadVarUint() x.DecodeBinary(r)
size := r.ReadVarUint() return r.Err
m := make([]KeyValue, size)
for i := range m {
m[i].Key = r.ReadString()
m[i].Value = r.ReadVarBytes()
}
if r.Err != nil {
return r.Err
}
x.Time = ts
x.Items = m
return nil
} }
func (x Meta) Bytes() []byte { func (x Meta) Bytes() []byte {
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
w.WriteVarUint(x.Time) x.EncodeBinary(w.BinWriter)
w.WriteVarUint(uint64(len(x.Items)))
for _, e := range x.Items {
w.WriteString(e.Key)
w.WriteVarBytes(e.Value)
}
return w.Bytes() return w.Bytes()
} }
@ -46,3 +28,59 @@ func (x Meta) GetAttr(name string) []byte {
} }
return nil return nil
} }
// DecodeBinary implements the io.Serializable interface.
func (x *Meta) DecodeBinary(r *io.BinReader) {
ts := r.ReadVarUint()
size := r.ReadVarUint()
m := make([]KeyValue, size)
for i := range m {
m[i].Key = r.ReadString()
m[i].Value = r.ReadVarBytes()
}
if r.Err != nil {
return
}
x.Time = ts
x.Items = m
}
// EncodeBinary implements the io.Serializable interface.
func (x Meta) EncodeBinary(w *io.BinWriter) {
w.WriteVarUint(x.Time)
w.WriteVarUint(uint64(len(x.Items)))
for _, e := range x.Items {
w.WriteString(e.Key)
w.WriteVarBytes(e.Value)
}
}
// Size returns size of x in bytes.
func (x Meta) Size() int {
size := getVarIntSize(x.Time)
size += getVarIntSize(uint64(len(x.Items)))
for i := range x.Items {
ln := len(x.Items[i].Key)
size += getVarIntSize(uint64(ln)) + ln
ln = len(x.Items[i].Value)
size += getVarIntSize(uint64(ln)) + ln
}
return size
}
// getVarIntSize returns the size in number of bytes of a variable integer.
// (reference: GetVarSize(int value), https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs)
func getVarIntSize(value uint64) int {
var size int
if value < 0xFD {
size = 1 // unit8
} else if value <= 0xFFFF {
size = 3 // byte + uint16
} else {
size = 5 // byte + uint32
}
return size
}