wire: Reduce allocs with contiguous slices.
The current code involves a ton of small allocations which is harsh on the garbage collector and in turn causes a lot of addition runtime overhead both in terms of additional memory and processing time. In order to improve the situation, this drasticially reduces the number of allocations by creating contiguous slices of objects and deserializing into them. Since the final data structures consist of slices of pointers to the objects, they are constructed by pointing them into the appropriate offset of the contiguous slice. This could be improved upon even further by converting all of the data structures provided the wire package to be slices of contiguous objects directly, however that would be a major breaking API change and would end up requiring updating a lot more code in every caller. I do think that ultimately the API should be changed, but the changes in this commit already makes a massive difference and it doesn't require touching any of the callers, so it is a good place to begin. The following is a before and after comparison of the allocations with the benchmarks that did not change removed: benchmark old allocs new allocs delta ----------------------------------------------------------- DeserializeTxLarge 16715 11146 -33.32% DecodeGetHeaders 501 2 -99.60% DecodeHeaders 2001 2 -99.90% DecodeGetBlocks 501 2 -99.60% DecodeAddr 3001 2002 -33.29% DecodeInv 50003 3 -99.99% DecodeNotFound 50002 3 -99.99% DecodeMerkleBlock 107 3 -97.20%
This commit is contained in:
parent
5de5b7354c
commit
2adfb3b56a
9 changed files with 56 additions and 30 deletions
|
@ -70,14 +70,15 @@ func (msg *MsgAddr) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
return messageError("MsgAddr.BtcDecode", str)
|
return messageError("MsgAddr.BtcDecode", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addrList := make([]NetAddress, count)
|
||||||
msg.AddrList = make([]*NetAddress, 0, count)
|
msg.AddrList = make([]*NetAddress, 0, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
na := NetAddress{}
|
na := &addrList[i]
|
||||||
err := readNetAddress(r, pver, &na, true)
|
err := readNetAddress(r, pver, na, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.AddAddress(&na)
|
msg.AddAddress(na)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,14 +65,17 @@ func (msg *MsgGetBlocks) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
return messageError("MsgGetBlocks.BtcDecode", str)
|
return messageError("MsgGetBlocks.BtcDecode", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of hashes to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
locatorHashes := make([]ShaHash, count)
|
||||||
msg.BlockLocatorHashes = make([]*ShaHash, 0, count)
|
msg.BlockLocatorHashes = make([]*ShaHash, 0, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
sha := ShaHash{}
|
hash := &locatorHashes[i]
|
||||||
err := readElement(r, &sha)
|
err := readElement(r, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.AddBlockLocatorHash(&sha)
|
msg.AddBlockLocatorHash(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = readElement(r, &msg.HashStop)
|
err = readElement(r, &msg.HashStop)
|
||||||
|
|
|
@ -49,14 +49,17 @@ func (msg *MsgGetData) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
return messageError("MsgGetData.BtcDecode", str)
|
return messageError("MsgGetData.BtcDecode", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of inventory vectors to deserialize into in
|
||||||
|
// order to reduce the number of allocations.
|
||||||
|
invList := make([]InvVect, count)
|
||||||
msg.InvList = make([]*InvVect, 0, count)
|
msg.InvList = make([]*InvVect, 0, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
iv := InvVect{}
|
iv := &invList[i]
|
||||||
err := readInvVect(r, pver, &iv)
|
err := readInvVect(r, pver, iv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.AddInvVect(&iv)
|
msg.AddInvVect(iv)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -62,14 +62,17 @@ func (msg *MsgGetHeaders) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
return messageError("MsgGetHeaders.BtcDecode", str)
|
return messageError("MsgGetHeaders.BtcDecode", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of hashes to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
locatorHashes := make([]ShaHash, count)
|
||||||
msg.BlockLocatorHashes = make([]*ShaHash, 0, count)
|
msg.BlockLocatorHashes = make([]*ShaHash, 0, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
sha := ShaHash{}
|
hash := &locatorHashes[i]
|
||||||
err := readElement(r, &sha)
|
err := readElement(r, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.AddBlockLocatorHash(&sha)
|
msg.AddBlockLocatorHash(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = readElement(r, &msg.HashStop)
|
err = readElement(r, &msg.HashStop)
|
||||||
|
|
|
@ -49,10 +49,13 @@ func (msg *MsgHeaders) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
return messageError("MsgHeaders.BtcDecode", str)
|
return messageError("MsgHeaders.BtcDecode", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of headers to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
headers := make([]BlockHeader, count)
|
||||||
msg.Headers = make([]*BlockHeader, 0, count)
|
msg.Headers = make([]*BlockHeader, 0, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
bh := BlockHeader{}
|
bh := &headers[i]
|
||||||
err := readBlockHeader(r, pver, &bh)
|
err := readBlockHeader(r, pver, bh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -68,7 +71,7 @@ func (msg *MsgHeaders) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
"transactions [count %v]", txCount)
|
"transactions [count %v]", txCount)
|
||||||
return messageError("MsgHeaders.BtcDecode", str)
|
return messageError("MsgHeaders.BtcDecode", str)
|
||||||
}
|
}
|
||||||
msg.AddBlockHeader(&bh)
|
msg.AddBlockHeader(bh)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -57,14 +57,17 @@ func (msg *MsgInv) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
return messageError("MsgInv.BtcDecode", str)
|
return messageError("MsgInv.BtcDecode", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of inventory vectors to deserialize into in
|
||||||
|
// order to reduce the number of allocations.
|
||||||
|
invList := make([]InvVect, count)
|
||||||
msg.InvList = make([]*InvVect, 0, count)
|
msg.InvList = make([]*InvVect, 0, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
iv := InvVect{}
|
iv := &invList[i]
|
||||||
err := readInvVect(r, pver, &iv)
|
err := readInvVect(r, pver, iv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.AddInvVect(&iv)
|
msg.AddInvVect(iv)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -68,14 +68,17 @@ func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
return messageError("MsgMerkleBlock.BtcDecode", str)
|
return messageError("MsgMerkleBlock.BtcDecode", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of hashes to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
hashes := make([]ShaHash, count)
|
||||||
msg.Hashes = make([]*ShaHash, 0, count)
|
msg.Hashes = make([]*ShaHash, 0, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
var sha ShaHash
|
hash := &hashes[i]
|
||||||
err := readElement(r, &sha)
|
err := readElement(r, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.AddTxHash(&sha)
|
msg.AddTxHash(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Flags, err = ReadVarBytes(r, pver, maxFlagsPerMerkleBlock,
|
msg.Flags, err = ReadVarBytes(r, pver, maxFlagsPerMerkleBlock,
|
||||||
|
|
|
@ -46,14 +46,17 @@ func (msg *MsgNotFound) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
return messageError("MsgNotFound.BtcDecode", str)
|
return messageError("MsgNotFound.BtcDecode", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of inventory vectors to deserialize into in
|
||||||
|
// order to reduce the number of allocations.
|
||||||
|
invList := make([]InvVect, count)
|
||||||
msg.InvList = make([]*InvVect, 0, count)
|
msg.InvList = make([]*InvVect, 0, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
iv := InvVect{}
|
iv := &invList[i]
|
||||||
err := readInvVect(r, pver, &iv)
|
err := readInvVect(r, pver, iv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.AddInvVect(&iv)
|
msg.AddInvVect(iv)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -263,14 +263,16 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
return messageError("MsgTx.BtcDecode", str)
|
return messageError("MsgTx.BtcDecode", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deserialize the inputs.
|
||||||
|
txIns := make([]TxIn, count)
|
||||||
msg.TxIn = make([]*TxIn, count)
|
msg.TxIn = make([]*TxIn, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
ti := TxIn{}
|
ti := &txIns[i]
|
||||||
err = readTxIn(r, pver, msg.Version, &ti)
|
err = readTxIn(r, pver, msg.Version, ti)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.TxIn[i] = &ti
|
msg.TxIn[i] = ti
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err = ReadVarInt(r, pver)
|
count, err = ReadVarInt(r, pver)
|
||||||
|
@ -288,14 +290,16 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error {
|
||||||
return messageError("MsgTx.BtcDecode", str)
|
return messageError("MsgTx.BtcDecode", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deserialize the outputs.
|
||||||
|
txOuts := make([]TxOut, count)
|
||||||
msg.TxOut = make([]*TxOut, count)
|
msg.TxOut = make([]*TxOut, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
to := TxOut{}
|
to := &txOuts[i]
|
||||||
err = readTxOut(r, pver, msg.Version, &to)
|
err = readTxOut(r, pver, msg.Version, to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.TxOut[i] = &to
|
msg.TxOut[i] = to
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.LockTime, err = binarySerializer.Uint32(r, littleEndian)
|
msg.LockTime, err = binarySerializer.Uint32(r, littleEndian)
|
||||||
|
|
Loading…
Reference in a new issue