From 2f743b482185096a04dacdae9afb7e08498ecc5b Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 25 Sep 2013 20:20:53 -0500 Subject: [PATCH] Pre-allocate space for high use slices. This commit modifies the code to choose sane defaults for the backing arrays for slices that involve a lot of appends such block locators, hash processing, and needed transactions. This is an optimization to avoid the overhead of growing the backing arrays and copying the data multiple times in the most common case. This also prevents a leak in Go GC which will likely ultimatley be fixed, but the efficiecy gains alone are worth the change. --- blocklocator.go | 3 ++- process.go | 7 ++++++- txlookup.go | 12 +++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/blocklocator.go b/blocklocator.go index 5ae9d5e21..a3258f253 100644 --- a/blocklocator.go +++ b/blocklocator.go @@ -29,7 +29,8 @@ type BlockLocator []*btcwire.ShaHash // consist of the passed hash func (b *BlockChain) BlockLocatorFromHash(hash *btcwire.ShaHash) BlockLocator { // The locator contains the requested hash at the very least. - locator := BlockLocator([]*btcwire.ShaHash{hash}) + locator := make(BlockLocator, 0, btcwire.MaxBlockLocatorsPerMsg) + locator = append(locator, hash) // Nothing more to do if a locator for the genesis hash was requested. if hash.IsEqual(b.chainParams().GenesisHash) { diff --git a/process.go b/process.go index 98e120f49..60894d685 100644 --- a/process.go +++ b/process.go @@ -38,10 +38,15 @@ func (b *BlockChain) blockExists(hash *btcwire.ShaHash) bool { // It repeats the process for the newly accepted blocks (to detect further // orphans which may no longer be orphans) until there are no more. func (b *BlockChain) processOrphans(hash *btcwire.ShaHash) error { - processHashes := []*btcwire.ShaHash{hash} + // Start with processing at least the passed hash. Leave a little room + // for additional orphan blocks that need to be processed without + // needing to grow the array in the common case. + processHashes := make([]*btcwire.ShaHash, 0, 10) + processHashes = append(processHashes, hash) for len(processHashes) > 0 { // Pop the first hash to process from the slice. processHash := processHashes[0] + processHashes[0] = nil // Prevent GC leak. processHashes = processHashes[1:] // Look up all orphans that are parented by the block we just diff --git a/txlookup.go b/txlookup.go index 50f5c1842..0cc08a883 100644 --- a/txlookup.go +++ b/txlookup.go @@ -215,10 +215,20 @@ func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Bloc txInFlight[*txHash] = i } + // Make a reasonable guess for the maximum number of needed input + // transactions to use as the starting point for the needed transactions + // array. The array will dynamically grow as needed, but it's much less + // overhead to avoid growing and copying the array multiple times in the + // common case. Each block usually has no more than ten inputs per + // transaction, so use that as a reasonable starting point. A block + // with 2,000 transactions would only result in around 156KB on a 64-bit + // system using this approach. + maxNeededHint := (len(transactions) - 1) * 10 + txNeededList := make([]*btcwire.ShaHash, 0, maxNeededHint) + // Loop through all of the transaction inputs (except for the coinbase // which has no inputs) collecting them into lists of what is needed and // what is already known (in-flight). - var txNeededList []*btcwire.ShaHash txStore := make(map[btcwire.ShaHash]*txData) for i, tx := range transactions[1:] { for _, txIn := range tx.TxIn {