Optimize writeElement.
This commit modifies the writeElement function to have a "fast path" which uses type assertions for all of the types which btcwire write so the more expensive reflection-based binary.Write can be avoided. Also, this changes all cases that were writing raw ShaHash (32-byte) arrays (which requires a stack copy) instead simply passing the pointer. The following benchmark results show the results for serializing a block header after these changes: Before: BenchmarkWriteBlockHeader 500000 5566 ns/op After: BenchmarkWriteBlockHeader 1000000 991 ns/op This is part of the ongoing effort to optimize serialization as noted in conformal/btcd#27.
This commit is contained in:
parent
7de20add63
commit
9ee6a8aeb6
5 changed files with 105 additions and 7 deletions
|
@ -107,7 +107,7 @@ func readBlockHeader(r io.Reader, pver uint32, bh *BlockHeader) error {
|
|||
// writeBlockHeader writes a bitcoin block header to w.
|
||||
func writeBlockHeader(w io.Writer, pver uint32, bh *BlockHeader) error {
|
||||
sec := uint32(bh.Timestamp.Unix())
|
||||
err := writeElements(w, bh.Version, bh.PrevBlock, bh.MerkleRoot,
|
||||
err := writeElements(w, bh.Version, &bh.PrevBlock, &bh.MerkleRoot,
|
||||
sec, bh.Bits, bh.Nonce)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
104
common.go
104
common.go
|
@ -17,9 +17,7 @@ import (
|
|||
const maxVarIntPayload = 9
|
||||
|
||||
// readElement reads the next sequence of bytes from r using little endian
|
||||
// depending on the concrete type of element pointed to. It also accepts a
|
||||
// scratch buffer that is used for the primitive values rather than creating
|
||||
// a new buffer on every call.
|
||||
// depending on the concrete type of element pointed to.
|
||||
func readElement(r io.Reader, element interface{}) error {
|
||||
var scratch [8]byte
|
||||
|
||||
|
@ -140,6 +138,106 @@ func readElements(r io.Reader, elements ...interface{}) error {
|
|||
|
||||
// writeElement writes the little endian representation of element to w.
|
||||
func writeElement(w io.Writer, element interface{}) error {
|
||||
var scratch [8]byte
|
||||
|
||||
// Attempt to read the element based on the concrete type via fast
|
||||
// type assertions first.
|
||||
switch e := element.(type) {
|
||||
case int32:
|
||||
b := scratch[0:4]
|
||||
binary.LittleEndian.PutUint32(b, uint32(e))
|
||||
_, err := w.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case uint32:
|
||||
b := scratch[0:4]
|
||||
binary.LittleEndian.PutUint32(b, e)
|
||||
_, err := w.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case int64:
|
||||
b := scratch[0:8]
|
||||
binary.LittleEndian.PutUint64(b, uint64(e))
|
||||
_, err := w.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case uint64:
|
||||
b := scratch[0:8]
|
||||
binary.LittleEndian.PutUint64(b, e)
|
||||
_, err := w.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
// Message header checksum.
|
||||
case [4]byte:
|
||||
_, err := w.Write(e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
// Message header command.
|
||||
case [commandSize]uint8:
|
||||
_, err := w.Write(e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
// IP address.
|
||||
case [16]byte:
|
||||
_, err := w.Write(e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case *ShaHash:
|
||||
_, err := w.Write(e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case ServiceFlag:
|
||||
b := scratch[0:8]
|
||||
binary.LittleEndian.PutUint64(b, uint64(e))
|
||||
_, err := w.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case InvType:
|
||||
b := scratch[0:4]
|
||||
binary.LittleEndian.PutUint32(b, uint32(e))
|
||||
_, err := w.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case BitcoinNet:
|
||||
b := scratch[0:4]
|
||||
binary.LittleEndian.PutUint32(b, uint32(e))
|
||||
_, err := w.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return binary.Write(w, binary.LittleEndian, element)
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ func readInvVect(r io.Reader, pver uint32, iv *InvVect) error {
|
|||
|
||||
// writeInvVect serializes an InvVect to w depending on the protocol version.
|
||||
func writeInvVect(w io.Writer, pver uint32, iv *InvVect) error {
|
||||
err := writeElements(w, iv.Type, iv.Hash)
|
||||
err := writeElements(w, iv.Type, &iv.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ func (msg *MsgGetBlocks) BtcEncode(w io.Writer, pver uint32) error {
|
|||
}
|
||||
}
|
||||
|
||||
err = writeElement(w, msg.HashStop)
|
||||
err = writeElement(w, &msg.HashStop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ func (msg *MsgGetHeaders) BtcEncode(w io.Writer, pver uint32) error {
|
|||
}
|
||||
}
|
||||
|
||||
err = writeElement(w, msg.HashStop)
|
||||
err = writeElement(w, &msg.HashStop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue