-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
multi: update main package and btcutil to chainhash/v1.1.0, use optimized dsha256 #2075
multi: update main package and btcutil to chainhash/v1.1.0, use optimized dsha256 #2075
Conversation
fa537e1
to
9922761
Compare
Pull Request Test Coverage Report for Build 7268575324
💛 - Coveralls |
Here's a benchmark run vs master:
|
Here's another comparison, old is w/o the last commit, new is w/ it:
|
Here's a run with the same computer as the first run above (master vs this PR), but this time with
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice optimization! One go module issue, the rest are just non-blocking ideas for further optimizations since we touch an area that is used very often.
btcutil/psbt/go.mod
Outdated
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 | ||
github.com/davecgh/go-spew v1.1.1 | ||
github.com/stretchr/testify v1.7.0 | ||
) | ||
|
||
require ( | ||
github.com/aead/siphash v1.0.1 // indirect |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Running go mod tidy on this file with Go 1.19 or 1.21 removed all these lines again for some reason... Should we bump the Go version of this package to fix? I think with 1.18 or 1.19 there was some change that made the two files more stable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird, I built with go version go1.21.5 darwin/arm64
.
I think maybe part of it is that our Go version declaration modules is a bit all over the place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, could be. I bumped everything to 1.19 in my module cleanup PR: #1825
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, no you're right, go mod tidy
eliminated a lot of stuff.
txscript/sighash.go
Outdated
@@ -289,7 +292,14 @@ func calcWitnessSignatureHashRaw(subScript []byte, sigHashes *TxSigHashes, | |||
binary.LittleEndian.PutUint32(bHashType[:], uint32(hashType)) | |||
sigHash.Write(bHashType[:]) | |||
|
|||
return chainhash.DoubleHashB(sigHash.Bytes()), nil | |||
sigHashBytes := chainhash.DoubleHashRaw(func(w io.Writer) error { | |||
// TODO(rosabeef): put entire calc func into this? then no |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should do this, yes. Also, there's another instance of DoubleHashB
just a few lines above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slight decrease here in time/op:
⛰ benchstat old.txt new.txt
name old time/op new time/op delta
CalcWitnessSigHash-8 31.9µs ± 2% 31.5µs ± 1% -1.31% (p=0.000 n=10+10)
name old alloc/op new alloc/op delta
CalcWitnessSigHash-8 19.9kB ± 0% 19.9kB ± 0% ~ (all equal)
name old allocs/op new allocs/op delta
CalcWitnessSigHash-8 801 ± 0% 801 ± 0% ~ (all equal)
txscript/sighash.go
Outdated
sigHashBytes := chainhash.DoubleHashRaw(func(w io.Writer) error { | ||
// First write out, then encode the transaction's version | ||
// number. | ||
var bVersion [4]byte |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use a couple of these 4 byte arrays. Since PutUint32
overwrites all bytes, I think it would be safe to re-use a single one and just call it scratch
? It's likely all on the stack so probably doesn't make a huge difference though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to use binary.Write
first, but that made things slower, so then I switched to just using a single [8]byte
and slicing into that.
Ended up with a nice speed up on all marks:
name old time/op new time/op delta
CalcWitnessSigHash-8 31.5µs ± 0% 29.2µs ± 0% -7.05% (p=0.000 n=10+10)
name old alloc/op new alloc/op delta
CalcWitnessSigHash-8 19.9kB ± 0% 18.5kB ± 0% -7.14% (p=0.000 n=10+10)
name old allocs/op new allocs/op delta
CalcWitnessSigHash-8 801 ± 0% 445 ± 0% -44.44% (p=0.000 n=10+10)
// The script code for a p2wkh is a length prefix | ||
// varint for the next 25 bytes, followed by a | ||
// re-creation of the original p2pkh pk script. | ||
w.Write([]byte{0x19}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this would make a big difference, but to avoid multiple allocations, we could do:
w.Write([]byte{0x19, OP_DUP, OP_HASH160, OP_DATA_20})
instead. But non-blocking of course.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can give it a shot to compare w/ a benchmark.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't make much off a diff seemingly:
name old time/op new time/op delta
CalcWitnessSigHash-8 29.1µs ± 0% 29.6µs ± 2% +1.70% (p=0.000 n=9+9)
name old alloc/op new alloc/op delta
CalcWitnessSigHash-8 18.5kB ± 0% 18.5kB ± 0% ~ (all equal)
name old allocs/op new allocs/op delta
CalcWitnessSigHash-8 445 ± 0% 445 ± 0% ~ (all equal)
I guess the compiler can optimize those instances.
In this commit, we update the top-level btcd package to use the latest version of btcutil and also the chainhash package. With this version bump, we can now use the new optimized dsha256 routine where applicable. With this commit, I've covered most of the areas we'll hash an entire transaction/block/header, but we may want to optimize some other areas further, in particular, the witness sighash calc.
…sighash In this commit, we optimize the sighash calc further by writing directly into the buffer used for serialization by the sha256.New() instance rather than to an intermediate buffer, which is then write to the hash buffer.
We can write direly into the hash writer vs serializing into a buffer, then writing that into the hash writer.
We used to use a lot of small buffers for serialization, but now we'll use one buffer large enough, and slice into it when needed. `` name old time/op new time/op delta CalcWitnessSigHash-8 31.5µs ± 0% 29.2µs ± 0% -7.05% (p=0.000 n=10+10) name old alloc/op new alloc/op delta CalcWitnessSigHash-8 19.9kB ± 0% 18.5kB ± 0% -7.14% (p=0.000 n=10+10) name old allocs/op new allocs/op delta CalcWitnessSigHash-8 801 ± 0% 445 ± 0% -44.44% (p=0.000 n=10+10) ```
9922761
to
19008ed
Compare
The final comparison:
|
In this commit, we update the top-level btcd package to use the latest
version of btcutil and also the chainhash package. With this version
bump, we can now use the new optimized dsha256 routine where applicable.
With this commit, I've covered most of the areas we'll hash an entire
transaction/block/header, but we may want to optimize some other areas
further, in particular, the witness sighash calc.