Commit bbf27ce0 authored by Kirill Smelkov's avatar Kirill Smelkov

X zodbdump: remove allocations from hot path

on non testdata/1.fs

name        old time/op    new time/op    delta
ZodbDump-4     313ms ± 1%      98ms ± 0%  -68.81%  (p=0.008 n=5+5)

name        old alloc/op   new alloc/op   delta
ZodbDump-4    75.0MB ± 0%     0.1MB ± 0%  -99.89%  (p=0.008 n=5+5)

name        old allocs/op  new allocs/op  delta
ZodbDump-4     2.16M ± 0%     0.00M ± 0%  -99.91%  (p=0.008 n=5+5)
parent 2121ce8a
// TODO copyright/license
// Package xfmt provide addons to std fmt and strconv package with focus on
// formatting text without allocations.
package xfmt
import (
......@@ -6,7 +10,29 @@ import (
"../xslice"
)
// appendHex appends hex representation of x to b
const (
hexdigits = "0123456789abcdef"
)
// Stringer is interface for natively formatting a value representation via xfmt
type Stringer interface {
// XFmtString method is used to append formatted value to destination buffer
// The grown buffer have to be returned
XFmtString(b []byte) []byte
}
// Append appends to b formatted x
//
// NOTE sadly since x is interface it makes real value substituted to it
// escape to heap (not so a problem since usually they already are) but then also
// if x has non-pointer receiver convT2I _allocates_ memory for the value copy.
//
// -> always pass to append &object, even if object has non-pointer XFmtString receiver.
func Append(b []byte, x Stringer) []byte {
return x.XFmtString(b)
}
// AppendHex appends to b hex representation of x
func AppendHex(b []byte, x []byte) []byte {
lx := hex.EncodedLen(len(x))
lb := len(b)
......@@ -14,3 +40,16 @@ func AppendHex(b []byte, x []byte) []byte {
hex.Encode(b[lb:], x)
return b
}
// AppendHex64 appends to b x formateed 16-character hex string
func AppendHex64(b []byte, x uint64) []byte {
// like sprintf("%016x") but faster and less allocations
l := len(b)
b = xslice.Grow(b, 16)
bb := b[l:]
for i := 15; i >= 0; i-- {
bb[i] = hexdigits[x & 0xf]
x >>= 4
}
return b
}
......@@ -38,11 +38,11 @@ import (
"io"
"log"
"os"
"strconv"
"../../../zodb"
"../../../storage/fs1"
"lab.nexedi.com/kirr/go123/mem"
"../../../xcommon/xfmt"
)
......@@ -52,29 +52,50 @@ type dumper struct {
HashOnly bool // whether to dump only hashes of data without content
afterFirst bool // true after first transaction has been dumped
workMem []byte // reusable data buffer for formatting
}
var _LF = []byte{'\n'}
// DumpData dumps one data record
func (d *dumper) DumpData(datai *zodb.StorageRecordInformation) error {
entry := "obj " + datai.Oid.String() + " "
buf := d.workMem[:0]
//entry := "obj " + datai.Oid.String() + " "
buf = append(buf, "obj "...)
buf = xfmt.Append(buf, &datai.Oid)
buf = append(buf, ' ')
writeData := false
switch {
case datai.Data == nil:
entry += "delete"
//entry += "delete"
buf = append(buf, "delete"...)
case datai.Tid != datai.DataTid:
entry += "from " + datai.DataTid.String()
//entry += "from " + datai.DataTid.String()
buf = append(buf, "from "...)
buf = xfmt.Append(buf, &datai.DataTid)
default:
entry += fmt.Sprintf("%d sha1:%x", len(datai.Data), sha1.Sum(datai.Data))
//entry += fmt.Sprintf("%d sha1:%x", len(datai.Data), sha1.Sum(datai.Data))
buf = strconv.AppendInt(buf, int64(len(datai.Data)), 10)
buf = append(buf, " sha1:"...)
dataSha1 := sha1.Sum(datai.Data)
buf = xfmt.AppendHex(buf, dataSha1[:])
writeData = true
}
entry += "\n"
//entry += "\n"
buf = append(buf, '\n')
// TODO use writev(data, "\n") via net.Buffers (it is already available)
_, err := d.W.Write(mem.Bytes(entry))
//_, err := d.W.Write(mem.Bytes(entry))
_, err := d.W.Write(buf)
if err != nil {
goto out
}
......@@ -85,13 +106,15 @@ func (d *dumper) DumpData(datai *zodb.StorageRecordInformation) error {
goto out
}
_, err = d.W.Write([]byte("\n"))
_, err = d.W.Write(_LF)
if err != nil {
goto out
}
}
out:
d.workMem = buf
// XXX do we need this context ?
// see for rationale in similar place in DumpTxn
if err != nil {
......
......@@ -10,16 +10,28 @@ import (
"encoding/binary"
"lab.nexedi.com/kirr/go123/xstrings"
"../xcommon/xfmt"
)
func (tid Tid) String() string {
// XXX also print "tid:" prefix ?
return fmt.Sprintf("%016x", uint64(tid))
//return fmt.Sprintf("%016x", uint64(tid))
return string(tid.XFmtString(nil))
}
func (oid Oid) String() string {
// XXX also print "oid:" prefix ?
return fmt.Sprintf("%016x", uint64(oid))
//return fmt.Sprintf("%016x", uint64(oid))
return string(oid.XFmtString(nil))
}
func (tid Tid) XFmtString(b []byte) []byte {
return xfmt.AppendHex64(b, uint64(tid))
}
func (oid Oid) XFmtString(b []byte) []byte {
return xfmt.AppendHex64(b, uint64(oid))
}
// XXX move me out of here
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment