Files
rippled/include/xrpl/tx/paths/detail/FlowDebugInfo.h

331 lines
9.6 KiB
C++

#pragma once
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/paths/detail/AmountSpec.h>
#include <boost/container/flat_map.hpp>
#include <chrono>
#include <optional>
#include <sstream>
namespace xrpl::path::detail {
// Track performance information of a single payment
struct FlowDebugInfo
{
using clock = std::chrono::high_resolution_clock;
using time_point = clock::time_point;
boost::container::flat_map<std::string, std::pair<time_point, time_point>> timePoints;
boost::container::flat_map<std::string, std::size_t> counts;
struct PassInfo
{
PassInfo() = delete;
PassInfo(bool nativeIn, bool nativeOut) : nativeIn(nativeIn), nativeOut(nativeOut)
{
}
bool const nativeIn;
bool const nativeOut;
std::vector<EitherAmount> in;
std::vector<EitherAmount> out;
std::vector<size_t> numActive;
std::vector<std::vector<EitherAmount>> liquiditySrcIn;
std::vector<std::vector<EitherAmount>> liquiditySrcOut;
void
reserve(size_t s)
{
in.reserve(s);
out.reserve(s);
liquiditySrcIn.reserve(s);
liquiditySrcOut.reserve(s);
numActive.reserve(s);
}
[[nodiscard]] size_t
size() const
{
return in.size();
}
void
pushBack(EitherAmount const& inAmt, EitherAmount const& outAmt, std::size_t active)
{
in.push_back(inAmt);
out.push_back(outAmt);
numActive.push_back(active);
}
void
pushLiquiditySrc(EitherAmount const& eIn, EitherAmount const& eOut)
{
XRPL_ASSERT(
!liquiditySrcIn.empty(),
"xrpl::path::detail::FlowDebugInfo::pushLiquiditySrc : "
"non-empty liquidity source");
liquiditySrcIn.back().push_back(eIn);
liquiditySrcOut.back().push_back(eOut);
}
void
newLiquidityPass()
{
auto const s = liquiditySrcIn.size();
size_t const r = !numActive.empty() ? numActive.back() : 16;
liquiditySrcIn.resize(s + 1);
liquiditySrcIn.back().reserve(r);
liquiditySrcOut.resize(s + 1);
liquiditySrcOut.back().reserve(r);
}
};
PassInfo passInfo;
FlowDebugInfo() = delete;
FlowDebugInfo(bool nativeIn, bool nativeOut) : passInfo(nativeIn, nativeOut)
{
timePoints.reserve(16);
counts.reserve(16);
passInfo.reserve(64);
}
[[nodiscard]] auto
duration(std::string const& tag) const
{
auto i = timePoints.find(tag);
if (i == timePoints.end())
{
// LCOV_EXCL_START
UNREACHABLE(
"xrpl::path::detail::FlowDebugInfo::duration : timepoint not "
"found");
return std::chrono::duration<double>(0);
// LCOV_EXCL_STOP
}
auto const& t = i->second;
return std::chrono::duration_cast<std::chrono::duration<double>>(t.second - t.first);
}
[[nodiscard]] std::size_t
count(std::string const& tag) const
{
auto i = counts.find(tag);
if (i == counts.end())
return 0;
return i->second;
}
// Time the duration of the existence of the result
auto
timeBlock(std::string name)
{
struct Stopper
{
std::string tag;
FlowDebugInfo* info;
Stopper(std::string name, FlowDebugInfo& pi) : tag(std::move(name)), info(&pi)
{
auto const start = FlowDebugInfo::clock::now();
info->timePoints.emplace(tag, std::make_pair(start, start));
}
~Stopper()
{
auto const end = FlowDebugInfo::clock::now();
info->timePoints[tag].second = end;
}
Stopper(Stopper&&) = default;
};
return Stopper(std::move(name), *this);
}
void
inc(std::string const& tag)
{
auto i = counts.find(tag);
if (i == counts.end())
{
counts[tag] = 1;
}
++i->second;
}
void
setCount(std::string const& tag, std::size_t c)
{
counts[tag] = c;
}
[[nodiscard]] std::size_t
passCount() const
{
return passInfo.size();
}
void
pushPass(EitherAmount const& in, EitherAmount const& out, std::size_t activeStrands)
{
passInfo.pushBack(in, out, activeStrands);
}
void
pushLiquiditySrc(EitherAmount const& in, EitherAmount const& out)
{
passInfo.pushLiquiditySrc(in, out);
}
void
newLiquidityPass()
{
passInfo.newLiquidityPass();
}
[[nodiscard]] std::string
toString(bool writePassInfo) const
{
std::ostringstream ostr;
auto const d = duration("main");
ostr << "duration: " << d.count() << ", pass_count: " << passCount();
if (writePassInfo)
{
auto writeList = [&ostr](auto const& vals, auto&& fun, char delim = ';') {
ostr << '[';
if (!vals.empty())
{
ostr << fun(vals[0]);
for (size_t i = 1, e = vals.size(); i < e; ++i)
ostr << delim << fun(vals[i]);
}
ostr << ']';
};
auto writeXrpAmtList = [&writeList](
std::vector<EitherAmount> const& amts, char delim = ';') {
auto getVal = [](EitherAmount const& a) -> std::string {
return xrpl::to_string(a.get<XRPAmount>());
};
writeList(amts, getVal, delim);
};
auto writeIouAmtList = [&writeList](
std::vector<EitherAmount> const& amts, char delim = ';') {
auto getVal = [](EitherAmount const& a) -> std::string {
return xrpl::to_string(a.get<IOUAmount>());
};
writeList(amts, getVal, delim);
};
auto writeIntList = [&writeList](std::vector<size_t> const& vals, char delim = ';') {
// NOLINTNEXTLINE(bugprone-return-const-ref-from-parameter)
auto getVal = [](size_t const& v) -> size_t const& { return v; };
writeList(vals, getVal);
};
auto writeNestedIouAmtList =
[&ostr, &writeIouAmtList](std::vector<std::vector<EitherAmount>> const& amts) {
ostr << '[';
if (!amts.empty())
{
writeIouAmtList(amts[0], '|');
for (size_t i = 1, e = amts.size(); i < e; ++i)
{
ostr << ';';
writeIouAmtList(amts[i], '|');
}
}
ostr << ']';
};
auto writeNestedXrpAmtList =
[&ostr, &writeXrpAmtList](std::vector<std::vector<EitherAmount>> const& amts) {
ostr << '[';
if (!amts.empty())
{
writeXrpAmtList(amts[0], '|');
for (size_t i = 1, e = amts.size(); i < e; ++i)
{
ostr << ';';
writeXrpAmtList(amts[i], '|');
}
}
ostr << ']';
};
ostr << ", in_pass: ";
if (passInfo.nativeIn)
{
writeXrpAmtList(passInfo.in);
}
else
{
writeIouAmtList(passInfo.in);
}
ostr << ", out_pass: ";
if (passInfo.nativeOut)
{
writeXrpAmtList(passInfo.out);
}
else
{
writeIouAmtList(passInfo.out);
}
ostr << ", num_active: ";
writeIntList(passInfo.numActive);
if (!passInfo.liquiditySrcIn.empty() && !passInfo.liquiditySrcIn.back().empty())
{
ostr << ", l_src_in: ";
if (passInfo.nativeIn)
{
writeNestedXrpAmtList(passInfo.liquiditySrcIn);
}
else
{
writeNestedIouAmtList(passInfo.liquiditySrcIn);
}
ostr << ", l_src_out: ";
if (passInfo.nativeOut)
{
writeNestedXrpAmtList(passInfo.liquiditySrcOut);
}
else
{
writeNestedIouAmtList(passInfo.liquiditySrcOut);
}
}
}
return ostr.str();
}
};
inline void
writeDiffElement(
std::ostringstream& ostr,
std::pair<std::tuple<AccountID, AccountID, Currency>, STAmount> const& elem)
{
using namespace std;
auto const k = elem.first;
auto const v = elem.second;
ostr << '[' << get<0>(k) << '|' << get<1>(k) << '|' << get<2>(k) << '|' << v << ']';
};
template <class Iter>
void
writeDiffs(std::ostringstream& ostr, Iter begin, Iter end)
{
ostr << '[';
if (begin != end)
{
writeDiffElement(ostr, *begin);
++begin;
}
for (; begin != end; ++begin)
{
ostr << ';';
writeDiffElement(ostr, *begin);
}
ostr << ']';
};
} // namespace xrpl::path::detail