Eliminate a minor quadratic time bug, caused by the unnecessary addition
of a (BtcWalletService) TxConfidenceListener for each list item in the
Transactions view. (Since the confidence listeners are internally held
in a CopyOnWriteArraySet, this sadly runs in quadratic time, slowing
down the Transactions view load a little.)
The confidence listener is apparently redundant because of a set of
calls to 'TransactionsListItem.cleanup' immediately upon construction of
the item list, which removes all the listeners just added. (This code
appears to date from at least February 2016, in commit c70df86.)
(The confidence indicators are kept up to date by simply reloading the
entire list upon each wallet change event.)
Use a crude Bloom filter (of sorts) to cut down the quadratic number of
calls to 'TransactionAwareTradable.isRelatedToTransaction' (that is, one
for each tx-tradable pair) during the Transactions view load. In this
way, we may reduce the number of calls roughly 40-fold, for a Bisq
instance with similar numbers of BSQ swap trades and escrow trades.
(Sadly, profiling does not show a 40-fold reduction in the size of the
'isRelatedToTransaction' hotspot, likely due to the remaining calls
being expensive ones involving disputed trades or unusual txs with
nonzero locktime, e.g. dust attacks or funds from Electrum wallets.)
To this end, partition the wallet transactions into 64 pseudo-randomly
chosen buckets (with a dedicated bucket for txs which might be delayed
payouts, namely those with nonzero locktime). Add an interface method,
'TransactionAwareTradable.getRelatedTransactionFilter', which returns an
IntStream of all the indices of buckets where a related tx may plausibly
be found. Where this is unclear, e.g. for trades involved in a dispute,
just return everything (that is, the range 0..63 inclusive).
Add a class, 'RelatedTransactionFilterSlices', that holds a provided
list of TransactionAwareTradable instances and 64 bitsets of all the
slices through their respective filters (each realised as 64-bit word
instead of a streams of integers). In this way, a list of tradables
plausibly related to any given tx may be quickly found by simply
selecting the appropriate bitset of the 64 (by the tx bucket index).
Inline a local variable, to eliminate another minor Sha256Hash.toString
hotspot in the Transactions view load, this time coming from
'TransactionsAwareOpenOffer.isRelatedToTransaction'. This is helpful in
the case that the user has a large number of (possibly disabled) BSQ
swap offers.
Move the line,
Set<Tradable> tradables = tradableRepository.getAll();
to the top level of 'TransactionsView.updateList', instead of needlessly
calling 'TradableRepository.getAll' (which builds a new set every
invocation) for each wallet transaction being iterated over.
This was causing a significant slowdown of the view load.
Use a mutable static tuple field to cache the last result of
'Sha256Hash.toString', which is used to get the ID string of the input
tx, when calling 'TransactionAwareTrade.isRelatedToTransaction'. In this
way, consecutive calls to 'isRelatedToTransaction' on the same input tx
(over all the past trades, as done by 'TransactionsView.updateList') are
sped up significantly, since hex encoding the txId is a bottleneck.
Replace the "Optional.ofNullable(...)..." constructs with more direct
code using short-circuit operators, as this is shorter and a little
faster. Also use "trade.get[Deposit|Payout]TxId()" instead of the code
"trade.get[Deposit|Payout]TxId().getTxId()", as (upon inspection of the
code) there should never be a case where the deposit/payout transaction
field of a Trade object is set but the respective txID field is null (or
set to an inconsistent value).
Also remove a redundant 'RefundManager.getDisputesAsObservableList'
method call, which was also slowing things down slightly.
The minor speedups afforded by the above are important because the
method 'TransactionAwareTrade.isRelatedToTransaction' is called a
quadratic number of times and consequently a major bottleneck when
loading the Transactions view.
Multiple threads read and write to the accounting blocks list causing
data races. Luckily, the LinkedList threw a ConcurrentModificationException
to limit damage. Now, a ReadWriteLock protects the LinkedList against
data races. Multiple threads can read the list at the same time but only
one thread can write to it. Other writing threads wait until it's their
turn.
Fixes#6545
Add a third cache to WalletService, cleared upon wallet change events,
which maps txIds to txs. This allows the speedup of repeated calls to
'WalletService.getConfidenceForTxId', by avoiding the need to copy and
scan the entire set of wallet txs to find the one with matching id,
for each method invocation.
This is mainly to provide a further speedup to the closed-trades-view
load, by making it much faster to filter the list of BSQ swap trades by
tx confirmation status in 'ClosedTradesDataModel.applyList' and
'ClosedTradesManager.getNumPastTrades'.
Reduce a bottleneck in the closed-trades-view load & table scrolling,
caused by a call to 'ClosedTradesManager.getNumPastTrades' for each
list item displayed. This repeatedly scans the entire collection of
closed trades and BSQ swap trades, in order to count all those which
share a given peer node address. (Scanning the BSQ swap trades is
particularly slow due to their filtering by tx confirmation status.)
To this end, cache the counts of trades by peer node address with a
nullable Multiset field in ClosedTradesManager & BsqSwapTradeManager,
so that the trades are scanned at most once when the view loads. Add
listeners to clear the respective cache when the trade list changes or
the BSQ wallet txs change.
Each time when MailboxMessageService.onAdded(...) got called with
multiple mailbox entries a new thread got created. That thread was never
shutdown. This change explicitly creates a new Thread and sets its
result with a SettableFuture. After its computation the thread
terminates.