Add time measurement profiler (#458)

Rebase
This commit is contained in:
cyan317
2022-12-20 18:57:47 +00:00
committed by GitHub
parent 37c765a072
commit 2f65a26dc7
14 changed files with 379 additions and 226 deletions

View File

@@ -9,7 +9,7 @@ formatter="clang-format -i"
first=$(git diff $sources) first=$(git diff $sources)
find $sources -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.ipp' \) -print0 | xargs -0 $formatter find $sources -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.ipp' \) -print0 | xargs -0 $formatter
second=$(git diff $sources) second=$(git diff $sources)
changes=$(diff <(echo "$first") <(echo "$second") | wc -l) changes=$(diff <(echo "$first") <(echo "$second") | wc -l | sed -e 's/^[[:space:]]*//')
if [ "$changes" != "0" ]; then if [ "$changes" != "0" ]; then
cat <<\EOF cat <<\EOF

View File

@@ -114,6 +114,7 @@ if(BUILD_TESTS)
unittests/Backend.cpp unittests/Backend.cpp
unittests/Logger.cpp unittests/Logger.cpp
unittests/Config.cpp unittests/Config.cpp
unittests/ProfilerTest.cpp
unittests/DOSGuard.cpp) unittests/DOSGuard.cpp)
include(CMake/deps/gtest.cmake) include(CMake/deps/gtest.cmake)
endif() endif()

View File

@@ -21,6 +21,7 @@
#include <backend/CassandraBackend.h> #include <backend/CassandraBackend.h>
#include <backend/DBHelpers.h> #include <backend/DBHelpers.h>
#include <log/Logger.h> #include <log/Logger.h>
#include <util/Profiler.h>
#include <functional> #include <functional>
#include <unordered_map> #include <unordered_map>
@@ -524,32 +525,30 @@ CassandraBackend::fetchTransactions(
std::vector<TransactionAndMetadata> results{numHashes}; std::vector<TransactionAndMetadata> results{numHashes};
std::vector<std::shared_ptr<ReadCallbackData<result_type>>> cbs; std::vector<std::shared_ptr<ReadCallbackData<result_type>>> cbs;
cbs.reserve(numHashes); cbs.reserve(numHashes);
auto start = std::chrono::system_clock::now(); auto timeDiff = util::timed([&]() {
for (std::size_t i = 0; i < hashes.size(); ++i)
{
CassandraStatement statement{selectTransaction_};
statement.bindNextBytes(hashes[i]);
for (std::size_t i = 0; i < hashes.size(); ++i) cbs.push_back(std::make_shared<ReadCallbackData<result_type>>(
{ numOutstanding, handler, [i, &results](auto& result) {
CassandraStatement statement{selectTransaction_}; if (result.hasResult())
statement.bindNextBytes(hashes[i]); results[i] = {
result.getBytes(),
result.getBytes(),
result.getUInt32(),
result.getUInt32()};
}));
cbs.push_back(std::make_shared<ReadCallbackData<result_type>>( executeAsyncRead(statement, processAsyncRead, *cbs[i]);
numOutstanding, handler, [i, &results](auto& result) { }
if (result.hasResult()) assert(results.size() == cbs.size());
results[i] = {
result.getBytes(),
result.getBytes(),
result.getUInt32(),
result.getUInt32()};
}));
executeAsyncRead(statement, processAsyncRead, *cbs[i]); // suspend the coroutine until completion handler is called.
} result.get();
assert(results.size() == cbs.size()); numReadRequestsOutstanding_ -= hashes.size();
});
// suspend the coroutine until completion handler is called.
result.get();
numReadRequestsOutstanding_ -= hashes.size();
auto end = std::chrono::system_clock::now();
for (auto const& cb : cbs) for (auto const& cb : cbs)
{ {
if (cb->errored) if (cb->errored)
@@ -557,10 +556,7 @@ CassandraBackend::fetchTransactions(
} }
log_.debug() << "Fetched " << numHashes log_.debug() << "Fetched " << numHashes
<< " transactions from Cassandra in " << " transactions from Cassandra in " << timeDiff
<< std::chrono::duration_cast<std::chrono::milliseconds>(
end - start)
.count()
<< " milliseconds"; << " milliseconds";
return results; return results;
} }

View File

@@ -31,6 +31,7 @@
#include <etl/ReportingETL.h> #include <etl/ReportingETL.h>
#include <log/Logger.h> #include <log/Logger.h>
#include <rpc/RPCHelpers.h> #include <rpc/RPCHelpers.h>
#include <util/Profiler.h>
#include <thread> #include <thread>
@@ -807,66 +808,68 @@ ETLSourceImpl<Derived>::loadInitialLedger(
backend_->cache().setFull(); backend_->cache().setFull();
if (!cacheOnly) if (!cacheOnly)
{ {
auto start = std::chrono::system_clock::now(); auto seconds = util::timed<std::chrono::seconds>([&]() {
for (auto& key : edgeKeys) for (auto& key : edgeKeys)
{
log_.debug() << "Writing edge key = " << ripple::strHex(key);
auto succ = backend_->cache().getSuccessor(
*ripple::uint256::fromVoidChecked(key), sequence);
if (succ)
backend_->writeSuccessor(
std::move(key), sequence, uint256ToString(succ->key));
}
ripple::uint256 prev = Backend::firstKey;
while (auto cur = backend_->cache().getSuccessor(prev, sequence))
{
assert(cur);
if (prev == Backend::firstKey)
{ {
backend_->writeSuccessor( log_.debug()
uint256ToString(prev), << "Writing edge key = " << ripple::strHex(key);
sequence, auto succ = backend_->cache().getSuccessor(
uint256ToString(cur->key)); *ripple::uint256::fromVoidChecked(key), sequence);
if (succ)
backend_->writeSuccessor(
std::move(key),
sequence,
uint256ToString(succ->key));
} }
ripple::uint256 prev = Backend::firstKey;
if (isBookDir(cur->key, cur->blob)) while (auto cur =
backend_->cache().getSuccessor(prev, sequence))
{ {
auto base = getBookBase(cur->key); assert(cur);
// make sure the base is not an actual object if (prev == Backend::firstKey)
if (!backend_->cache().get(cur->key, sequence))
{ {
auto succ = backend_->writeSuccessor(
backend_->cache().getSuccessor(base, sequence); uint256ToString(prev),
assert(succ); sequence,
if (succ->key == cur->key) uint256ToString(cur->key));
{
log_.debug() << "Writing book successor = "
<< ripple::strHex(base) << " - "
<< ripple::strHex(cur->key);
backend_->writeSuccessor(
uint256ToString(base),
sequence,
uint256ToString(cur->key));
}
} }
++numWrites;
if (isBookDir(cur->key, cur->blob))
{
auto base = getBookBase(cur->key);
// make sure the base is not an actual object
if (!backend_->cache().get(cur->key, sequence))
{
auto succ =
backend_->cache().getSuccessor(base, sequence);
assert(succ);
if (succ->key == cur->key)
{
log_.debug() << "Writing book successor = "
<< ripple::strHex(base) << " - "
<< ripple::strHex(cur->key);
backend_->writeSuccessor(
uint256ToString(base),
sequence,
uint256ToString(cur->key));
}
}
++numWrites;
}
prev = std::move(cur->key);
if (numWrites % 100000 == 0 && numWrites != 0)
log_.info()
<< "Wrote " << numWrites << " book successors";
} }
prev = std::move(cur->key);
if (numWrites % 100000 == 0 && numWrites != 0)
log_.info() << "Wrote " << numWrites << " book successors";
}
backend_->writeSuccessor( backend_->writeSuccessor(
uint256ToString(prev), uint256ToString(prev),
sequence, sequence,
uint256ToString(Backend::lastKey)); uint256ToString(Backend::lastKey));
++numWrites; ++numWrites;
auto end = std::chrono::system_clock::now(); });
auto seconds =
std::chrono::duration_cast<std::chrono::seconds>(end - start)
.count();
log_.info() log_.info()
<< "Looping through cache and submitting all writes took " << "Looping through cache and submitting all writes took "
<< seconds << seconds

View File

@@ -24,6 +24,7 @@
#include <etl/ReportingETL.h> #include <etl/ReportingETL.h>
#include <log/Logger.h> #include <log/Logger.h>
#include <subscriptions/SubscriptionManager.h> #include <subscriptions/SubscriptionManager.h>
#include <util/Profiler.h>
#include <boost/asio/connect.hpp> #include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
@@ -137,40 +138,39 @@ ReportingETL::loadInitialLedger(uint32_t startingSequence)
log_.debug() << "Deserialized ledger header. " << detail::toString(lgrInfo); log_.debug() << "Deserialized ledger header. " << detail::toString(lgrInfo);
auto start = std::chrono::system_clock::now(); auto timeDiff = util::timed<std::chrono::duration<double>>([&]() {
backend_->startWrites();
backend_->startWrites(); log_.debug() << "Started writes";
log_.debug() << "Started writes"; backend_->writeLedger(
lgrInfo, std::move(*ledgerData->mutable_ledger_header()));
backend_->writeLedger( log_.debug() << "Wrote ledger";
lgrInfo, std::move(*ledgerData->mutable_ledger_header())); FormattedTransactionsData insertTxResult =
insertTransactions(lgrInfo, *ledgerData);
log_.debug() << "Inserted txns";
log_.debug() << "Wrote ledger"; // download the full account state map. This function downloads full
FormattedTransactionsData insertTxResult = // ledger data and pushes the downloaded data into the writeQueue.
insertTransactions(lgrInfo, *ledgerData); // asyncWriter consumes from the queue and inserts the data into the
log_.debug() << "Inserted txns"; // Ledger object. Once the below call returns, all data has been pushed
// into the queue
loadBalancer_->loadInitialLedger(startingSequence);
// download the full account state map. This function downloads full ledger log_.debug() << "Loaded initial ledger";
// data and pushes the downloaded data into the writeQueue. asyncWriter
// consumes from the queue and inserts the data into the Ledger object.
// Once the below call returns, all data has been pushed into the queue
loadBalancer_->loadInitialLedger(startingSequence);
log_.debug() << "Loaded initial ledger"; if (!stopping_)
{
if (!stopping_) backend_->writeAccountTransactions(
{ std::move(insertTxResult.accountTxData));
backend_->writeAccountTransactions( backend_->writeNFTs(std::move(insertTxResult.nfTokensData));
std::move(insertTxResult.accountTxData)); backend_->writeNFTTransactions(
backend_->writeNFTs(std::move(insertTxResult.nfTokensData)); std::move(insertTxResult.nfTokenTxData));
backend_->writeNFTTransactions(std::move(insertTxResult.nfTokenTxData)); }
} backend_->finishWrites(startingSequence);
backend_->finishWrites(startingSequence); });
log_.debug() << "Time to download and store ledger = " << timeDiff;
auto end = std::chrono::system_clock::now();
log_.debug() << "Time to download and store ledger = "
<< ((end - start).count()) / 1000000000.0;
return lgrInfo; return lgrInfo;
} }
@@ -530,10 +530,8 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
backend_->writeNFTTransactions(std::move(insertTxResult.nfTokenTxData)); backend_->writeNFTTransactions(std::move(insertTxResult.nfTokenTxData));
log_.debug() << "wrote account_tx"; log_.debug() << "wrote account_tx";
auto start = std::chrono::system_clock::now(); auto [success, duration] = util::timed<std::chrono::duration<double>>(
bool success = backend_->finishWrites(lgrInfo.seq); [&]() { return backend_->finishWrites(lgrInfo.seq); });
auto end = std::chrono::system_clock::now();
auto duration = ((end - start).count()) / 1000000000.0;
log_.debug() << "Finished writes. took " << std::to_string(duration); log_.debug() << "Finished writes. took " << std::to_string(duration);
log_.debug() << "Finished ledger update. " << detail::toString(lgrInfo); log_.debug() << "Finished ledger update. " << detail::toString(lgrInfo);
@@ -620,12 +618,10 @@ ReportingETL::runETLPipeline(uint32_t startSequence, int numExtractors)
currentSequence) && currentSequence) &&
!writeConflict && !isStopping()) !writeConflict && !isStopping())
{ {
auto start = std::chrono::system_clock::now(); auto [fetchResponse, time] =
std::optional<org::xrpl::rpc::v1::GetLedgerResponse> util::timed<std::chrono::duration<double>>([&]() {
fetchResponse{fetchLedgerDataAndDiff(currentSequence)}; return fetchLedgerDataAndDiff(currentSequence);
auto end = std::chrono::system_clock::now(); });
auto time = ((end - start).count()) / 1000000000.0;
totalTime += time; totalTime += time;
// if the fetch is unsuccessful, stop. fetchLedger only // if the fetch is unsuccessful, stop. fetchLedger only

View File

@@ -21,6 +21,7 @@
#include <backend/BackendInterface.h> #include <backend/BackendInterface.h>
#include <log/Logger.h> #include <log/Logger.h>
#include <rpc/RPCHelpers.h> #include <rpc/RPCHelpers.h>
#include <util/Profiler.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
@@ -798,14 +799,10 @@ traverseOwnedNodes(
.count() .count()
<< " milliseconds"; << " milliseconds";
start = std::chrono::system_clock::now(); auto [objects, timeDiff] = util::timed(
auto objects = backend.fetchLedgerObjects(keys, sequence, yield); [&]() { return backend.fetchLedgerObjects(keys, sequence, yield); });
end = std::chrono::system_clock::now();
gLog.debug() << "Time loading owned entries: " gLog.debug() << "Time loading owned entries: " << timeDiff
<< std::chrono::duration_cast<std::chrono::milliseconds>(
end - start)
.count()
<< " milliseconds"; << " milliseconds";
for (auto i = 0; i < objects.size(); ++i) for (auto i = 0; i < objects.size(); ++i)
@@ -1652,60 +1649,58 @@ traverseTransactions(
boost::json::array txns; boost::json::array txns;
auto [blobs, retCursor] = transactionFetcher( auto [blobs, retCursor] = transactionFetcher(
context.backend, limit, forward, cursor, context.yield); context.backend, limit, forward, cursor, context.yield);
auto serializationStart = std::chrono::system_clock::now(); auto timeDiff = util::timed([&, &retCursor = retCursor, &blobs = blobs]() {
if (retCursor)
if (retCursor)
{
boost::json::object cursorJson;
cursorJson[JS(ledger)] = retCursor->ledgerSequence;
cursorJson[JS(seq)] = retCursor->transactionIndex;
response[JS(marker)] = cursorJson;
}
for (auto const& txnPlusMeta : blobs)
{
if ((txnPlusMeta.ledgerSequence < minIndex && !forward) ||
(txnPlusMeta.ledgerSequence > maxIndex && forward))
{ {
response.erase(JS(marker)); boost::json::object cursorJson;
break; cursorJson[JS(ledger)] = retCursor->ledgerSequence;
} cursorJson[JS(seq)] = retCursor->transactionIndex;
else if (txnPlusMeta.ledgerSequence > maxIndex && !forward) response[JS(marker)] = cursorJson;
{
gLog.debug() << "Skipping over transactions from incomplete ledger";
continue;
} }
boost::json::object obj; for (auto const& txnPlusMeta : blobs)
if (!binary)
{ {
auto [txn, meta] = toExpandedJson(txnPlusMeta); if ((txnPlusMeta.ledgerSequence < minIndex && !forward) ||
obj[JS(meta)] = meta; (txnPlusMeta.ledgerSequence > maxIndex && forward))
obj[JS(tx)] = txn; {
obj[JS(tx)].as_object()[JS(ledger_index)] = response.erase(JS(marker));
txnPlusMeta.ledgerSequence; break;
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date; }
} else if (txnPlusMeta.ledgerSequence > maxIndex && !forward)
else {
{ gLog.debug()
obj[JS(meta)] = ripple::strHex(txnPlusMeta.metadata); << "Skipping over transactions from incomplete ledger";
obj[JS(tx_blob)] = ripple::strHex(txnPlusMeta.transaction); continue;
obj[JS(ledger_index)] = txnPlusMeta.ledgerSequence; }
obj[JS(date)] = txnPlusMeta.date;
}
obj[JS(validated)] = true;
txns.push_back(obj);
}
response[JS(ledger_index_min)] = minIndex; boost::json::object obj;
response[JS(ledger_index_max)] = maxIndex;
response[JS(transactions)] = txns; if (!binary)
{
auto [txn, meta] = toExpandedJson(txnPlusMeta);
obj[JS(meta)] = meta;
obj[JS(tx)] = txn;
obj[JS(tx)].as_object()[JS(ledger_index)] =
txnPlusMeta.ledgerSequence;
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
}
else
{
obj[JS(meta)] = ripple::strHex(txnPlusMeta.metadata);
obj[JS(tx_blob)] = ripple::strHex(txnPlusMeta.transaction);
obj[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
obj[JS(date)] = txnPlusMeta.date;
}
obj[JS(validated)] = true;
txns.push_back(obj);
}
response[JS(ledger_index_min)] = minIndex;
response[JS(ledger_index_max)] = maxIndex;
response[JS(transactions)] = txns;
});
gLog.info() << "serialization took " << timeDiff
gLog.info() << "serialization took "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - serializationStart)
.count()
<< " milliseconds"; << " milliseconds";
return response; return response;

View File

@@ -19,6 +19,7 @@
#include <log/Logger.h> #include <log/Logger.h>
#include <rpc/RPCHelpers.h> #include <rpc/RPCHelpers.h>
#include <util/Profiler.h>
using namespace clio; using namespace clio;
@@ -37,27 +38,23 @@ doAccountTx(Context const& context)
return status; return status;
constexpr std::string_view outerFuncName = __func__; constexpr std::string_view outerFuncName = __func__;
auto const maybeResponse = auto const maybeResponse = traverseTransactions(
traverseTransactions( context,
context, [&accountID, &outerFuncName](
[&accountID, &outerFuncName]( std::shared_ptr<Backend::BackendInterface const> const& backend,
std::shared_ptr<Backend::BackendInterface const> const& backend, std::uint32_t const limit,
std::uint32_t const limit, bool const forward,
bool const forward, std::optional<Backend::TransactionsCursor> const& cursorIn,
std::optional<Backend::TransactionsCursor> const& cursorIn, boost::asio::yield_context& yield) {
boost::asio::yield_context& yield) { auto [txnsAndCursor, timeDiff] = util::timed([&]() {
auto const start = std::chrono::system_clock::now(); return backend->fetchAccountTransactions(
auto const txnsAndCursor = backend->fetchAccountTransactions(
accountID, limit, forward, cursorIn, yield); accountID, limit, forward, cursorIn, yield);
gLog.info()
<< outerFuncName << " db fetch took "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - start)
.count()
<< " milliseconds - num blobs = "
<< txnsAndCursor.txns.size();
return txnsAndCursor;
}); });
gLog.info() << outerFuncName << " db fetch took " << timeDiff
<< " milliseconds - num blobs = "
<< txnsAndCursor.txns.size();
return txnsAndCursor;
});
if (auto const status = std::get_if<Status>(&maybeResponse); status) if (auto const status = std::get_if<Status>(&maybeResponse); status)
return *status; return *status;

View File

@@ -19,6 +19,7 @@
#include <log/Logger.h> #include <log/Logger.h>
#include <rpc/RPCHelpers.h> #include <rpc/RPCHelpers.h>
#include <util/Profiler.h>
using namespace clio; using namespace clio;
@@ -38,28 +39,25 @@ doNFTHistory(Context const& context)
auto const tokenID = std::get<ripple::uint256>(maybeTokenID); auto const tokenID = std::get<ripple::uint256>(maybeTokenID);
constexpr std::string_view outerFuncName = __func__; constexpr std::string_view outerFuncName = __func__;
auto const maybeResponse = auto const maybeResponse = traverseTransactions(
traverseTransactions( context,
context, [&tokenID, &outerFuncName](
[&tokenID, &outerFuncName]( std::shared_ptr<Backend::BackendInterface const> const& backend,
std::shared_ptr<Backend::BackendInterface const> const& backend, std::uint32_t const limit,
std::uint32_t const limit, bool const forward,
bool const forward, std::optional<Backend::TransactionsCursor> const& cursorIn,
std::optional<Backend::TransactionsCursor> const& cursorIn, boost::asio::yield_context& yield)
boost::asio::yield_context& yield) -> Backend::TransactionsAndCursor {
-> Backend::TransactionsAndCursor { auto const [txnsAndCursor, timeDiff] =
auto const start = std::chrono::system_clock::now(); util::timed([&, &tokenID = tokenID]() {
auto const txnsAndCursor = backend->fetchNFTTransactions( return backend->fetchNFTTransactions(
tokenID, limit, forward, cursorIn, yield); tokenID, limit, forward, cursorIn, yield);
gLog.info() });
<< outerFuncName << " db fetch took " gLog.info() << outerFuncName << " db fetch took " << timeDiff
<< std::chrono::duration_cast<std::chrono::milliseconds>( << " milliseconds - num blobs = "
std::chrono::system_clock::now() - start) << txnsAndCursor.txns.size();
.count() return txnsAndCursor;
<< " milliseconds - num blobs = " });
<< txnsAndCursor.txns.size();
return txnsAndCursor;
});
if (auto const status = std::get_if<Status>(&maybeResponse); status) if (auto const status = std::get_if<Status>(&maybeResponse); status)
return *status; return *status;

59
src/util/Profiler.h Normal file
View File

@@ -0,0 +1,59 @@
//------------------------------------------------------------------------------
/*
This file is part of clio: https://github.com/XRPLF/clio
Copyright (c) 2022, the clio developers.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#pragma once
#include <chrono>
#include <type_traits>
#include <utility>
namespace util {
/**
* @brief Profiler function to measure the time consuming
* @param func function object, can be a lamdba or function wrapper
* @return return a pair if function wrapper has return value: result of
* function wrapper and the elapsed time(ms) during executing the given
* function only return the elapsed time if function wrapper does not have
* return value
*/
template <typename U = std::chrono::milliseconds, typename F>
[[nodiscard]] auto
timed(F&& func)
{
auto start = std::chrono::system_clock::now();
if constexpr (std::is_same_v<decltype(func()), void>)
{
func();
return std::chrono::duration_cast<U>(
std::chrono::system_clock::now() - start)
.count();
}
else
{
auto ret = func();
auto elapsed = std::chrono::duration_cast<U>(
std::chrono::system_clock::now() - start)
.count();
return std::make_pair(ret, elapsed);
}
}
} // namespace util

View File

@@ -108,7 +108,6 @@ public:
} }
{ {
auto it = ipConnCount_.find(ip); auto it = ipConnCount_.find(ip);
assert(it != ipConnCount_.end());
if (it != ipConnCount_.end()) if (it != ipConnCount_.end())
{ {
connsOk = it->second <= maxConnCount_; connsOk = it->second <= maxConnCount_;

View File

@@ -42,6 +42,7 @@
#include <rpc/Counters.h> #include <rpc/Counters.h>
#include <rpc/RPC.h> #include <rpc/RPC.h>
#include <rpc/WorkQueue.h> #include <rpc/WorkQueue.h>
#include <util/Profiler.h>
#include <util/Taggable.h> #include <util/Taggable.h>
#include <vector> #include <vector>
#include <webserver/DOSGuard.h> #include <webserver/DOSGuard.h>
@@ -437,11 +438,10 @@ handle_request(
RPC::makeError(RPC::RippledError::rpcBAD_SYNTAX)))); RPC::makeError(RPC::RippledError::rpcBAD_SYNTAX))));
boost::json::object response; boost::json::object response;
auto start = std::chrono::system_clock::now(); auto [v, timeDiff] =
auto v = RPC::buildResponse(*context); util::timed([&]() { return RPC::buildResponse(*context); });
auto end = std::chrono::system_clock::now();
auto us = auto us = std::chrono::duration<int, std::milli>(timeDiff);
std::chrono::duration_cast<std::chrono::microseconds>(end - start);
RPC::logDuration(*context, us); RPC::logDuration(*context, us);
if (auto status = std::get_if<RPC::Status>(&v)) if (auto status = std::get_if<RPC::Status>(&v))

View File

@@ -28,6 +28,7 @@
#include <rpc/WorkQueue.h> #include <rpc/WorkQueue.h>
#include <subscriptions/Message.h> #include <subscriptions/Message.h>
#include <subscriptions/SubscriptionManager.h> #include <subscriptions/SubscriptionManager.h>
#include <util/Profiler.h>
#include <util/Taggable.h> #include <util/Taggable.h>
#include <webserver/DOSGuard.h> #include <webserver/DOSGuard.h>
@@ -343,11 +344,10 @@ public:
response = getDefaultWsResponse(id); response = getDefaultWsResponse(id);
auto start = std::chrono::system_clock::now(); auto [v, timeDiff] =
auto v = RPC::buildResponse(*context); util::timed([&]() { return RPC::buildResponse(*context); });
auto end = std::chrono::system_clock::now();
auto us = std::chrono::duration_cast<std::chrono::microseconds>( auto us = std::chrono::duration<int, std::milli>(timeDiff);
end - start);
logDuration(*context, us); logDuration(*context, us);
if (auto status = std::get_if<RPC::Status>(&v)) if (auto status = std::get_if<RPC::Status>(&v))

108
unittests/ProfilerTest.cpp Normal file
View File

@@ -0,0 +1,108 @@
//------------------------------------------------------------------------------
/*
This file is part of clio: https://github.com/XRPLF/clio
Copyright (c) 2022, the clio developers.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <gtest/gtest.h>
#include <thread>
#include <util/Profiler.h>
using namespace util;
TEST(TimedTest, HasReturnValue)
{
auto [ret, time] = timed([]() {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
return 8;
});
ASSERT_EQ(ret, 8);
ASSERT_NE(time, 0);
}
TEST(TimedTest, ReturnVoid)
{
auto time = timed(
[]() { std::this_thread::sleep_for(std::chrono::milliseconds(5)); });
ASSERT_NE(time, 0);
}
struct FunctorTest
{
void
operator()() const
{
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
};
TEST(TimedTest, Functor)
{
auto time = timed(FunctorTest());
ASSERT_NE(time, 0);
}
TEST(TimedTest, MovedLambda)
{
auto f = []() {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
return 8;
};
auto [ret, time] = timed(std::move(f));
ASSERT_EQ(ret, 8);
ASSERT_NE(time, 0);
}
TEST(TimedTest, ChangeToNs)
{
auto f = []() {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
return 8;
};
auto [ret, time] = timed<std::chrono::nanoseconds>(std::move(f));
ASSERT_EQ(ret, 8);
ASSERT_GE(time, 5 * 1000000);
}
TEST(TimedTest, NestedLambda)
{
double timeNested;
auto f = [&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
timeNested = timed([]() {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
});
return 8;
};
auto [ret, time] = timed<std::chrono::nanoseconds>(std::move(f));
ASSERT_EQ(ret, 8);
ASSERT_GE(timeNested, 5);
ASSERT_GE(time, 10 * 1000000);
}
TEST(TimedTest, FloatSec)
{
auto f = []() {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
return 8;
};
auto [ret, time] = timed<std::chrono::duration<double>>(std::move(f));
ASSERT_EQ(ret, 8);
ASSERT_GE(time, 0);
}

View File

@@ -127,6 +127,7 @@ struct AsyncAsioContextTest : public NoLoggerFixture
{ {
work.reset(); work.reset();
ctx.stop(); ctx.stop();
runner.join();
} }
protected: protected:
@@ -134,7 +135,7 @@ protected:
private: private:
std::optional<boost::asio::io_service::work> work; std::optional<boost::asio::io_service::work> work;
std::jthread runner{[this] { ctx.run(); }}; std::thread runner{[this] { ctx.run(); }};
}; };
/** /**