I instrumented my import using the -initblock=FILE patch posted last night, putting printf tracepoints in TxnBegin, TxnCommit, TxnAbort, Read and Write:
ProcessBlock: ACCEPTED
CDB::Write()
DB4: txn_begin
CDB::Write()
CDB::Write()
CDB::Write()
DB4: txn_commit
SetBestChain: new best=000000005b5c1859db19 height=1751 work=7524897523416
ProcessBlock: ACCEPTED
CDB::Write()
DB4: txn_begin
CDB::Write()
CDB::Write()
CDB::Write()
DB4: txn_commit
SetBestChain: new best=00000000f396ab6b62ba height=1752 work=7529192556249
ProcessBlock: ACCEPTED
CDB::Write()
DB4: txn_begin
CDB::Write()
CDB::Write()
CDB::Write()
DB4: txn_commit
SetBestChain: new best=000000000c6bcf972117 height=1753 work=7533487589082
So, it appears that we have a CDB::Write() that occurs outside of a transaction (vTxn is empty??).
txnid==NULL is perfectly legal for db4, but it does mean that callpath may be operating outside of the DB_TXN_NOSYNC flag that is set in ::TxnBegin(). Thus, a CDB::Write() outside of a transaction may have synchronous behavior (DB_TXN_SYNC) as governed by DB_AUTO_COMMIT database flag.
EDIT: Wrapping WriteBlockIndex() inside a transaction does seem to speed up local disk import (-initblocks).
--- a/main.cpp
+++ b/main.cpp
@@ -1427,7 +1427,10 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned
pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork
CTxDB txdb;
+ txdb.TxnBegin();
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
+ if (!txdb.TxnCommit())
+ return false;
Of course that implies begin+commit+begin+commit in quick succession (SetBestChain), so maybe a less naive approach might be preferred (nested transactions, or wrap both db4 writes in the same transaction).