Skip to content

Conversation

@jrick
Copy link
Member

@jrick jrick commented Jan 8, 2026

This commit contains several performance and memory optimizations to improve message deserialization, both when generically called through the BtcDecode method of the Message interface and when reading wire protocol using ReadMessageN.

Special cases have been added for the bytes.Buffer and bytes.Reader reader types, avoiding the allocation and synchronization costs associated with using temporary buffers from the binary freelist and avoiding the heap allocations required due to the reader interface leaking the slice parameter.

ReadMessageN has reduced the number of allocations it must incur by avoiding the readElements helper function.

Finally, a new internal buffer type is introduced that is used exclusively by ReadMessageN. Since the lifetime and memory of this buffer is under the control of the wire package, and the buffer is never reset or truncated, this allows transaction script deserialization to slice the internal bytes of this buffer rather than pulling temporary buffers from the scriptPool freelist and copying these into a new contiguous allocation.

This commit contains several performance and memory optimizations to improve
message deserialization, both when generically called through the BtcDecode
method of the Message interface and when reading wire protocol using
ReadMessageN.

Special cases have been added for the bytes.Buffer and bytes.Reader reader
types, avoiding the allocation and synchronization costs associated with using
temporary buffers from the binary freelist and avoiding the heap allocations
required due to the reader interface leaking the slice parameter.

ReadMessageN has reduced the number of allocations it must incur by avoiding
the readElements helper function.

Finally, a new internal buffer type is introduced that is used exclusively by
ReadMessageN.  Since the lifetime and memory of this buffer is under the
control of the wire package, and the buffer is never reset or truncated, this
allows transaction script deserialization to slice the internal bytes of this
buffer rather than pulling temporary buffers from the scriptPool freelist and
copying these into a new contiguous allocation.
@jrick
Copy link
Member Author

jrick commented Jan 8, 2026

Using the newly added benchmark, before:

$ go test -run=^$ -bench=BenchmarkReadMessageN -benchmem 
goos: openbsd
goarch: amd64
pkg: github.com/decred/dcrd/wire
cpu: AMD Ryzen 7 5800X3D 8-Core Processor           
BenchmarkReadMessageN-8   	  334888	      3370 ns/op	     920 B/op	      15 allocs/op
PASS
ok  	github.com/decred/dcrd/wire	1.180s

After:

$ go test -run=^$ -bench=BenchmarkReadMessageN -benchmem 
goos: openbsd
goarch: amd64
pkg: github.com/decred/dcrd/wire
cpu: AMD Ryzen 7 5800X3D 8-Core Processor           
BenchmarkReadMessageN-8   	  537861	      1988 ns/op	    1432 B/op	       9 allocs/op
PASS
ok  	github.com/decred/dcrd/wire	1.106s

this is no longer technically true (the internal type is also allowed, and
will always be passed here) and tests would discover this requirement if it
were ever changed in ReadMessageN.
@davecgh davecgh added this to the 2.2.0 milestone Jan 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants