Make rescan faster by avoiding hash copies.

Copying the RIPEMD160 after SHA256 hash result into a new stack array
to be used as a map lookup key can be quite expensive, and this should
be avoided if possible on intensive tasks such as rescans.  This
change takes advantage of the new Hash160 methods of the
AddressPubKeyHash and AddressScriptHash types to use the address's
underlying hash array directly, rather than creating a copy from the
ScriptAddress result.

Unfortunately, for AddressPubKey, ScriptAddress may return either a
byte slice of len 33 or 65 depending on whether the pubkey is
compressed or not, so no such straightforward optimization is
possible.

As a result of this change, I have seen rescans perform roughly 3.5x
faster than before.
This commit is contained in:
Josh Rickmar 2014-04-20 15:51:04 -05:00
parent 1e75ccc9b9
commit f88db561f1

View file

@ -1479,9 +1479,10 @@ type rescanKeys struct {
// rescanBlock rescans all transactions in a single block. This is a helper // rescanBlock rescans all transactions in a single block. This is a helper
// function for handleRescan. // function for handleRescan.
func rescanBlock(wsc *wsClient, lookups *rescanKeys, blk *btcutil.Block) { func rescanBlock(wsc *wsClient, lookups *rescanKeys, blk *btcutil.Block) {
// Vars used for map keys. The item being checked is copied into // Vars used for map keys. If necessary, the byte slice for each
// these and then used as the lookup key. Saves on GC. // checked item is copied into and then used as the lookup key.
var ripemd160Hash [ripemd160.Size]byte // This is quite a bit more efficient than creating an address
// string as it saves on GC and performs less copying.
var compressedPubkey [33]byte var compressedPubkey [33]byte
var uncompressedPubkey [65]byte var uncompressedPubkey [65]byte
var outpoint btcwire.OutPoint var outpoint btcwire.OutPoint
@ -1532,14 +1533,12 @@ func rescanBlock(wsc *wsClient, lookups *rescanKeys, blk *btcutil.Block) {
for _, addr := range addrs { for _, addr := range addrs {
switch a := addr.(type) { switch a := addr.(type) {
case *btcutil.AddressPubKeyHash: case *btcutil.AddressPubKeyHash:
copy(ripemd160Hash[:], a.ScriptAddress()) if _, ok := lookups.pubKeyHashes[*a.Hash160()]; !ok {
if _, ok := lookups.pubKeyHashes[ripemd160Hash]; !ok {
continue continue
} }
case *btcutil.AddressScriptHash: case *btcutil.AddressScriptHash:
copy(ripemd160Hash[:], a.ScriptAddress()) if _, ok := lookups.scriptHashes[*a.Hash160()]; !ok {
if _, ok := lookups.scriptHashes[ripemd160Hash]; !ok {
continue continue
} }
@ -1642,7 +1641,6 @@ func handleRescan(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.Error)
uncompressedPubkeys: map[[65]byte]struct{}{}, uncompressedPubkeys: map[[65]byte]struct{}{},
unspent: map[btcwire.OutPoint]struct{}{}, unspent: map[btcwire.OutPoint]struct{}{},
} }
var ripemd160Hash [ripemd160.Size]byte
var compressedPubkey [33]byte var compressedPubkey [33]byte
var uncompressedPubkey [65]byte var uncompressedPubkey [65]byte
for _, addrStr := range cmd.Addresses { for _, addrStr := range cmd.Addresses {
@ -1656,12 +1654,10 @@ func handleRescan(wsc *wsClient, icmd btcjson.Cmd) (interface{}, *btcjson.Error)
} }
switch a := addr.(type) { switch a := addr.(type) {
case *btcutil.AddressPubKeyHash: case *btcutil.AddressPubKeyHash:
copy(ripemd160Hash[:], a.ScriptAddress()) lookups.pubKeyHashes[*a.Hash160()] = struct{}{}
lookups.pubKeyHashes[ripemd160Hash] = struct{}{}
case *btcutil.AddressScriptHash: case *btcutil.AddressScriptHash:
copy(ripemd160Hash[:], a.ScriptAddress()) lookups.scriptHashes[*a.Hash160()] = struct{}{}
lookups.scriptHashes[ripemd160Hash] = struct{}{}
case *btcutil.AddressPubKey: case *btcutil.AddressPubKey:
pubkeyBytes := a.ScriptAddress() pubkeyBytes := a.ScriptAddress()