I made a mistake when working through the locking code. I assumed that if a transaction was scheduled to take a lock, that it actually needed that lock. This turns out to be not quite as true as one would have hoped.
I'm now walking through a bunch of the transactions and looking for locks that were requested but which aren't actually required. This should improve performance substantially once I find them all.