Skip to content

Commit 6300438

Browse files
committed
Merge bitcoin/bitcoin#30115: rpc: avoid copying into UniValue
d7707d9 rpc: avoid copying into UniValue (Cory Fields) Pull request description: These are the simple (and hopefully obviously correct) copies that can be moves instead. This is a follow-up from bitcoin/bitcoin#30094 (comment) As it turns out, there are hundreds of places where we copy UniValues needlessly. It should be the case that moves are always preferred over copies, so there should be no downside to these changes. willcl-ark, however, noticed that memory usage may increase in some cases. Logically this makes no sense to me. The only plausible explanation imo is that because the moves are faster, more ops/second occur in some cases. This list of moves was obtained by changing the function signatures of the UniValue functions to accept only rvalues, then compiling and fixing them up one by one. There still exist many places where copies are being made. These can/should be fixed up, but weren't done here for the sake of doing the easy ones first. I ran these changes through clang-tidy with `performance-move-const-arg` and `bugprone-use-after-move` and no bugs were detected (though that's obviously not to say it can be trusted 100%). As stated above, there are still lots of other less trivial fixups to do after these including: - Using non-const UniValues where possible so that moves can happen - Refactoring code in order to be able to move a UniValue without introducing a use-after-move - Refactoring functions to accept UniValues by value rather than by const reference ACKs for top commit: achow101: ACK d7707d9 ryanofsky: Code review ACK d7707d9. No changes since last review other than rebase. I agree benchmarks showing increased peak memory usage and RSS are surprising, but number of allocations is down as expected, and runtime is also decreased. willcl-ark: ACK d7707d9 Tree-SHA512: 7f511be73984553c278186286a7d161a34b2574c7f5f1a0edc87c2913b4c025a0af5241ef9af2df17547f2e4ef79710aa5bbb762fc9472435781c0488dba3435
2 parents e163d86 + d7707d9 commit 6300438

27 files changed

+183
-183
lines changed

src/bitcoin-cli.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ class AddrinfoRequestHandler : public BaseRequestHandler
297297
total += counts.at(i);
298298
}
299299
addresses.pushKV("total", total);
300-
result.pushKV("addresses_known", addresses);
300+
result.pushKV("addresses_known", std::move(addresses));
301301
return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V1_LEGACY);
302302
}
303303
};
@@ -348,7 +348,7 @@ class GetinfoRequestHandler: public BaseRequestHandler
348348
connections.pushKV("in", batch[ID_NETWORKINFO]["result"]["connections_in"]);
349349
connections.pushKV("out", batch[ID_NETWORKINFO]["result"]["connections_out"]);
350350
connections.pushKV("total", batch[ID_NETWORKINFO]["result"]["connections"]);
351-
result.pushKV("connections", connections);
351+
result.pushKV("connections", std::move(connections));
352352

353353
result.pushKV("networks", batch[ID_NETWORKINFO]["result"]["networks"]);
354354
result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
@@ -940,7 +940,7 @@ static void GetWalletBalances(UniValue& result)
940940
const UniValue& balance = getbalances.find_value("result")["mine"]["trusted"];
941941
balances.pushKV(wallet_name, balance);
942942
}
943-
result.pushKV("balances", balances);
943+
result.pushKV("balances", std::move(balances));
944944
}
945945

946946
/**

src/core_write.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,14 @@ void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry
201201
UniValue o(UniValue::VOBJ);
202202
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
203203
o.pushKV("hex", HexStr(txin.scriptSig));
204-
in.pushKV("scriptSig", o);
204+
in.pushKV("scriptSig", std::move(o));
205205
}
206206
if (!tx.vin[i].scriptWitness.IsNull()) {
207207
UniValue txinwitness(UniValue::VARR);
208208
for (const auto& item : tx.vin[i].scriptWitness.stack) {
209209
txinwitness.push_back(HexStr(item));
210210
}
211-
in.pushKV("txinwitness", txinwitness);
211+
in.pushKV("txinwitness", std::move(txinwitness));
212212
}
213213
if (have_undo) {
214214
const Coin& prev_coin = txundo->vprevout[i];
@@ -224,14 +224,14 @@ void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry
224224
p.pushKV("generated", bool(prev_coin.fCoinBase));
225225
p.pushKV("height", uint64_t(prev_coin.nHeight));
226226
p.pushKV("value", ValueFromAmount(prev_txout.nValue));
227-
p.pushKV("scriptPubKey", o_script_pub_key);
228-
in.pushKV("prevout", p);
227+
p.pushKV("scriptPubKey", std::move(o_script_pub_key));
228+
in.pushKV("prevout", std::move(p));
229229
}
230230
}
231231
in.pushKV("sequence", (int64_t)txin.nSequence);
232-
vin.push_back(in);
232+
vin.push_back(std::move(in));
233233
}
234-
entry.pushKV("vin", vin);
234+
entry.pushKV("vin", std::move(vin));
235235

236236
UniValue vout(UniValue::VARR);
237237
for (unsigned int i = 0; i < tx.vout.size(); i++) {
@@ -244,14 +244,14 @@ void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry
244244

245245
UniValue o(UniValue::VOBJ);
246246
ScriptToUniv(txout.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
247-
out.pushKV("scriptPubKey", o);
248-
vout.push_back(out);
247+
out.pushKV("scriptPubKey", std::move(o));
248+
vout.push_back(std::move(out));
249249

250250
if (have_undo) {
251251
amt_total_out += txout.nValue;
252252
}
253253
}
254-
entry.pushKV("vout", vout);
254+
entry.pushKV("vout", std::move(vout));
255255

256256
if (have_undo) {
257257
const CAmount fee = amt_total_in - amt_total_out;

src/net_types.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ UniValue BanMapToJson(const banmap_t& bans)
4343
const auto& ban_entry = it.second;
4444
UniValue j = ban_entry.ToJson();
4545
j.pushKV(BANMAN_JSON_ADDR_KEY, address.ToString());
46-
bans_json.push_back(j);
46+
bans_json.push_back(std::move(j));
4747
}
4848
return bans_json;
4949
}

src/rest.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -934,10 +934,10 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
934934
// include the script in a json output
935935
UniValue o(UniValue::VOBJ);
936936
ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
937-
utxo.pushKV("scriptPubKey", o);
938-
utxos.push_back(utxo);
937+
utxo.pushKV("scriptPubKey", std::move(o));
938+
utxos.push_back(std::move(utxo));
939939
}
940-
objGetUTXOResponse.pushKV("utxos", utxos);
940+
objGetUTXOResponse.pushKV("utxos", std::move(utxos));
941941

942942
// return json string
943943
std::string strJSON = objGetUTXOResponse.write() + "\n";

src/rpc/blockchain.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,9 +1025,9 @@ static RPCHelpMan gettxoutsetinfo()
10251025
unspendables.pushKV("bip30", ValueFromAmount(stats.total_unspendables_bip30 - prev_stats.total_unspendables_bip30));
10261026
unspendables.pushKV("scripts", ValueFromAmount(stats.total_unspendables_scripts - prev_stats.total_unspendables_scripts));
10271027
unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.total_unspendables_unclaimed_rewards - prev_stats.total_unspendables_unclaimed_rewards));
1028-
block_info.pushKV("unspendables", unspendables);
1028+
block_info.pushKV("unspendables", std::move(unspendables));
10291029

1030-
ret.pushKV("block_info", block_info);
1030+
ret.pushKV("block_info", std::move(block_info));
10311031
}
10321032
} else {
10331033
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
@@ -1111,7 +1111,7 @@ static RPCHelpMan gettxout()
11111111
ret.pushKV("value", ValueFromAmount(coin.out.nValue));
11121112
UniValue o(UniValue::VOBJ);
11131113
ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
1114-
ret.pushKV("scriptPubKey", o);
1114+
ret.pushKV("scriptPubKey", std::move(o));
11151115
ret.pushKV("coinbase", (bool)coin.fCoinBase);
11161116

11171117
return ret;
@@ -1161,7 +1161,7 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
11611161
// one below the activation height
11621162
rv.pushKV("active", DeploymentActiveAfter(blockindex, chainman, dep));
11631163
rv.pushKV("height", chainman.GetConsensus().DeploymentHeight(dep));
1164-
softforks.pushKV(DeploymentName(dep), rv);
1164+
softforks.pushKV(DeploymentName(dep), std::move(rv));
11651165
}
11661166

11671167
static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const ChainstateManager& chainman, Consensus::DeploymentPos id)
@@ -1214,7 +1214,7 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
12141214
statsUV.pushKV("threshold", statsStruct.threshold);
12151215
statsUV.pushKV("possible", statsStruct.possible);
12161216
}
1217-
bip9.pushKV("statistics", statsUV);
1217+
bip9.pushKV("statistics", std::move(statsUV));
12181218

12191219
std::string sig;
12201220
sig.reserve(signals.size());
@@ -1230,9 +1230,9 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
12301230
rv.pushKV("height", chainman.m_versionbitscache.StateSinceHeight(blockindex, chainman.GetConsensus(), id));
12311231
}
12321232
rv.pushKV("active", ThresholdState::ACTIVE == next_state);
1233-
rv.pushKV("bip9", bip9);
1233+
rv.pushKV("bip9", std::move(bip9));
12341234

1235-
softforks.pushKV(DeploymentName(id), rv);
1235+
softforks.pushKV(DeploymentName(id), std::move(rv));
12361236
}
12371237

12381238
// used by rest.cpp:rest_chaininfo, so cannot be static
@@ -1498,7 +1498,7 @@ static RPCHelpMan getchaintips()
14981498
}
14991499
obj.pushKV("status", status);
15001500

1501-
res.push_back(obj);
1501+
res.push_back(std::move(obj));
15021502
}
15031503

15041504
return res;
@@ -1978,7 +1978,7 @@ static RPCHelpMan getblockstats()
19781978
ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte
19791979
ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
19801980
ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex());
1981-
ret_all.pushKV("feerate_percentiles", feerates_res);
1981+
ret_all.pushKV("feerate_percentiles", std::move(feerates_res));
19821982
ret_all.pushKV("height", (int64_t)pindex.nHeight);
19831983
ret_all.pushKV("ins", inputs);
19841984
ret_all.pushKV("maxfee", maxfee);
@@ -2262,9 +2262,9 @@ static RPCHelpMan scantxoutset()
22622262
unspent.pushKV("coinbase", coin.IsCoinBase());
22632263
unspent.pushKV("height", (int32_t)coin.nHeight);
22642264

2265-
unspents.push_back(unspent);
2265+
unspents.push_back(std::move(unspent));
22662266
}
2267-
result.pushKV("unspents", unspents);
2267+
result.pushKV("unspents", std::move(unspents));
22682268
result.pushKV("total_amount", ValueFromAmount(total_in));
22692269
} else {
22702270
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid action '%s'", action));
@@ -2504,7 +2504,7 @@ static RPCHelpMan scanblocks()
25042504

25052505
ret.pushKV("from_height", start_block_height);
25062506
ret.pushKV("to_height", start_index->nHeight); // start_index is always the last scanned block here
2507-
ret.pushKV("relevant_blocks", blocks);
2507+
ret.pushKV("relevant_blocks", std::move(blocks));
25082508
ret.pushKV("completed", completed);
25092509
}
25102510
else {

src/rpc/client.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<s
391391
// Use pushKVEnd instead of pushKV to avoid overwriting an explicit
392392
// "args" value with an implicit one. Let the RPC server handle the
393393
// request as given.
394-
params.pushKVEnd("args", positional_args);
394+
params.pushKVEnd("args", std::move(positional_args));
395395
}
396396

397397
return params;

src/rpc/external_signer.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@ static RPCHelpMan enumeratesigners()
5353
UniValue signer_res = UniValue::VOBJ;
5454
signer_res.pushKV("fingerprint", signer.m_fingerprint);
5555
signer_res.pushKV("name", signer.m_name);
56-
signers_res.push_back(signer_res);
56+
signers_res.push_back(std::move(signer_res));
5757
}
5858
} catch (const std::exception& e) {
5959
throw JSONRPCError(RPC_MISC_ERROR, e.what());
6060
}
6161
UniValue result(UniValue::VOBJ);
62-
result.pushKV("signers", signers_res);
62+
result.pushKV("signers", std::move(signers_res));
6363
return result;
6464
}
6565
};

src/rpc/fees.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ static RPCHelpMan estimatesmartfee()
8888
result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
8989
} else {
9090
errors.push_back("Insufficient data or no feerate found");
91-
result.pushKV("errors", errors);
91+
result.pushKV("errors", std::move(errors));
9292
}
9393
result.pushKV("blocks", feeCalc.returnedTarget);
9494
return result;
@@ -198,18 +198,18 @@ static RPCHelpMan estimaterawfee()
198198
horizon_result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
199199
horizon_result.pushKV("decay", buckets.decay);
200200
horizon_result.pushKV("scale", (int)buckets.scale);
201-
horizon_result.pushKV("pass", passbucket);
201+
horizon_result.pushKV("pass", std::move(passbucket));
202202
// buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output
203-
if (buckets.fail.start != -1) horizon_result.pushKV("fail", failbucket);
203+
if (buckets.fail.start != -1) horizon_result.pushKV("fail", std::move(failbucket));
204204
} else {
205205
// Output only information that is still meaningful in the event of error
206206
horizon_result.pushKV("decay", buckets.decay);
207207
horizon_result.pushKV("scale", (int)buckets.scale);
208-
horizon_result.pushKV("fail", failbucket);
208+
horizon_result.pushKV("fail", std::move(failbucket));
209209
errors.push_back("Insufficient data or no feerate found which meets threshold");
210-
horizon_result.pushKV("errors", errors);
210+
horizon_result.pushKV("errors", std::move(errors));
211211
}
212-
result.pushKV(StringForFeeEstimateHorizon(horizon), horizon_result);
212+
result.pushKV(StringForFeeEstimateHorizon(horizon), std::move(horizon_result));
213213
}
214214
return result;
215215
},

src/rpc/mempool.cpp

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ static RPCHelpMan testmempoolaccept()
202202
auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
203203
if (exit_early || it == package_result.m_tx_results.end()) {
204204
// Validation unfinished. Just return the txid and wtxid.
205-
rpc_result.push_back(result_inner);
205+
rpc_result.push_back(std::move(result_inner));
206206
continue;
207207
}
208208
const auto& tx_result = it->second;
@@ -229,8 +229,8 @@ static RPCHelpMan testmempoolaccept()
229229
for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) {
230230
effective_includes_res.push_back(wtxid.ToString());
231231
}
232-
fees.pushKV("effective-includes", effective_includes_res);
233-
result_inner.pushKV("fees", fees);
232+
fees.pushKV("effective-includes", std::move(effective_includes_res));
233+
result_inner.pushKV("fees", std::move(fees));
234234
}
235235
} else {
236236
result_inner.pushKV("allowed", false);
@@ -241,7 +241,7 @@ static RPCHelpMan testmempoolaccept()
241241
result_inner.pushKV("reject-reason", state.GetRejectReason());
242242
}
243243
}
244-
rpc_result.push_back(result_inner);
244+
rpc_result.push_back(std::move(result_inner));
245245
}
246246
return rpc_result;
247247
},
@@ -295,7 +295,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
295295
fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
296296
fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors()));
297297
fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
298-
info.pushKV("fees", fees);
298+
info.pushKV("fees", std::move(fees));
299299

300300
const CTransaction& tx = e.GetTx();
301301
std::set<std::string> setDepends;
@@ -311,14 +311,14 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
311311
depends.push_back(dep);
312312
}
313313

314-
info.pushKV("depends", depends);
314+
info.pushKV("depends", std::move(depends));
315315

316316
UniValue spent(UniValue::VARR);
317317
for (const CTxMemPoolEntry& child : e.GetMemPoolChildrenConst()) {
318318
spent.push_back(child.GetTx().GetHash().ToString());
319319
}
320320

321-
info.pushKV("spentby", spent);
321+
info.pushKV("spentby", std::move(spent));
322322

323323
// Add opt-in RBF status
324324
bool rbfStatus = false;
@@ -347,7 +347,7 @@ UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempoo
347347
// Mempool has unique entries so there is no advantage in using
348348
// UniValue::pushKV, which checks if the key already exists in O(N).
349349
// UniValue::pushKVEnd is used instead which currently is O(1).
350-
o.pushKVEnd(e.GetTx().GetHash().ToString(), info);
350+
o.pushKVEnd(e.GetTx().GetHash().ToString(), std::move(info));
351351
}
352352
return o;
353353
} else {
@@ -364,7 +364,7 @@ UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempoo
364364
return a;
365365
} else {
366366
UniValue o(UniValue::VOBJ);
367-
o.pushKV("txids", a);
367+
o.pushKV("txids", std::move(a));
368368
o.pushKV("mempool_sequence", mempool_sequence);
369369
return o;
370370
}
@@ -474,7 +474,7 @@ static RPCHelpMan getmempoolancestors()
474474
const uint256& _hash = e.GetTx().GetHash();
475475
UniValue info(UniValue::VOBJ);
476476
entryToJSON(mempool, info, e);
477-
o.pushKV(_hash.ToString(), info);
477+
o.pushKV(_hash.ToString(), std::move(info));
478478
}
479479
return o;
480480
}
@@ -539,7 +539,7 @@ static RPCHelpMan getmempooldescendants()
539539
const uint256& _hash = e.GetTx().GetHash();
540540
UniValue info(UniValue::VOBJ);
541541
entryToJSON(mempool, info, e);
542-
o.pushKV(_hash.ToString(), info);
542+
o.pushKV(_hash.ToString(), std::move(info));
543543
}
544544
return o;
545545
}
@@ -653,7 +653,7 @@ static RPCHelpMan gettxspendingprevout()
653653
o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
654654
}
655655

656-
result.push_back(o);
656+
result.push_back(std::move(o));
657657
}
658658

659659
return result;
@@ -992,20 +992,20 @@ static RPCHelpMan submitpackage()
992992
for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) {
993993
effective_includes_res.push_back(wtxid.ToString());
994994
}
995-
fees.pushKV("effective-includes", effective_includes_res);
995+
fees.pushKV("effective-includes", std::move(effective_includes_res));
996996
}
997-
result_inner.pushKV("fees", fees);
997+
result_inner.pushKV("fees", std::move(fees));
998998
for (const auto& ptx : it->second.m_replaced_transactions) {
999999
replaced_txids.insert(ptx->GetHash());
10001000
}
10011001
break;
10021002
}
1003-
tx_result_map.pushKV(tx->GetWitnessHash().GetHex(), result_inner);
1003+
tx_result_map.pushKV(tx->GetWitnessHash().GetHex(), std::move(result_inner));
10041004
}
1005-
rpc_result.pushKV("tx-results", tx_result_map);
1005+
rpc_result.pushKV("tx-results", std::move(tx_result_map));
10061006
UniValue replaced_list(UniValue::VARR);
10071007
for (const uint256& hash : replaced_txids) replaced_list.push_back(hash.ToString());
1008-
rpc_result.pushKV("replaced-transactions", replaced_list);
1008+
rpc_result.pushKV("replaced-transactions", std::move(replaced_list));
10091009
return rpc_result;
10101010
},
10111011
};

0 commit comments

Comments
 (0)