Compare commits

...

17 Commits

Author SHA1 Message Date
TimothyBanks
543366c1fb fix: Merge upstream branch 2026-07-28 13:40:06 -04:00
TimothyBanks
5820b9cad7 fix: Merge upstream branch 2026-07-28 13:30:00 -04:00
TimothyBanks
338eb50814 fix: Merge upstream branch 2026-07-28 13:01:50 -04:00
TimothyBanks
de2a913ad4 fix: Addressing code review comments 2026-07-27 12:41:27 -04:00
TimothyBanks
2d8fad840a feat: Add some performance tests for escrow wasm functions 2026-07-23 15:05:57 -04:00
Timothy Banks
bc6455b8b9 Merge branch 'ripple/smart-escrow' into smart-escrow/perf-test 2026-07-23 10:12:39 -04:00
TimothyBanks
ca97c98b68 feat: Add some performance tests for escrow wasm functions 2026-07-23 09:39:41 -04:00
TimothyBanks
26f9488b5d feat: Add some performance tests for escrow wasm functions 2026-07-22 15:59:52 -04:00
TimothyBanks
fca18f59cd feat: Add some performance tests for escrow wasm functions 2026-07-22 15:57:08 -04:00
TimothyBanks
d1d3e3c72e feat: Run levelization 2026-07-16 17:14:45 -04:00
TimothyBanks
b9df9c9c45 feat: Dump metric values after test run 2026-07-16 12:57:10 -04:00
TimothyBanks
3bb6904946 feat: Dump metric values after test run 2026-07-16 12:45:46 -04:00
TimothyBanks
d430010b36 feat: Add metrics around wasm calls 2026-07-16 11:22:13 -04:00
TimothyBanks
d88f7d8f68 feat: Use beast metrics 2026-07-16 11:11:32 -04:00
TimothyBanks
1825f38a10 fix: Add perf tests for escrow create and escrow finish 2026-07-10 23:06:22 -04:00
TimothyBanks
e70623d0cb fix: Add perf tests for escrow create and escrow finish 2026-07-10 16:43:30 -04:00
TimothyBanks
19631a6d92 fix: Add perf tests for escrow create and escrow finish 2026-07-10 16:21:53 -04:00
15 changed files with 239 additions and 13 deletions

View File

@@ -207,6 +207,7 @@ xrpl.conditions > xrpl.basics
xrpl.conditions > xrpl.protocol
xrpl.config > xrpl.basics
xrpl.core > xrpl.basics
xrpl.core > xrpl.config
xrpl.core > xrpl.json
xrpl.core > xrpl.protocol
xrpl.json > xrpl.basics
@@ -310,6 +311,7 @@ xrpld.rpc > xrpl.server
xrpld.rpc > xrpl.shamap
xrpld.rpc > xrpl.tx
xrpld.shamap > xrpl.basics
xrpld.shamap > xrpl.core
xrpld.shamap > xrpld.core
xrpld.shamap > xrpl.nodestore
xrpld.shamap > xrpl.protocol

View File

@@ -91,14 +91,19 @@ def main():
if not os.environ.get("TIDY"):
return 0
repo_root = Path(
subprocess.check_output(
["git", "rev-parse", "--show-toplevel"],
cwd=Path(__file__).parent,
text=True,
).strip()
# Derive the repo root from git so this keeps working regardless of where
# under the tree this script lives. Fall back to the script location
# (bin/pre-commit/ is two levels below the repo root) if git is unavailable.
result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
capture_output=True,
text=True,
cwd=Path(__file__).parent,
)
if result.returncode == 0:
repo_root = Path(result.stdout.strip())
else:
repo_root = Path(__file__).parent.parent.parent
files = staged_files(repo_root)
if not files:
return 0

View File

@@ -3,6 +3,7 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/insight/Event.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
@@ -107,6 +108,12 @@ public:
return j_;
}
[[nodiscard]] virtual beast::insight::Event
executionTimeEvent(std::string_view name) const
{
return {};
}
// LCOV_EXCL_START
[[nodiscard]] virtual bool

View File

@@ -2,7 +2,9 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/insight/Event.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/CollectorManager.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
@@ -83,6 +85,15 @@ public:
{
}
beast::insight::Event
executionTimeEvent(std::string_view name) const override
{
return ctx_.registry.get()
.getCollectorManager()
.group(std::string{name.data(), name.size()})
->makeEvent("finish_time");
}
bool
checkSelf() const override
{

View File

@@ -11,6 +11,7 @@
#include <xrpl/beast/insight/Meter.h>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <utility>
@@ -87,6 +88,7 @@ public:
Collector::ptr collector;
Items items;
mutable std::mutex groupMutex;
explicit GroupsImp(Collector::ptr collector) : collector(std::move(collector))
{
@@ -97,6 +99,7 @@ public:
Group::ptr const&
get(std::string const& name) override
{
std::scoped_lock const _{groupMutex};
std::pair<Items::iterator, bool> const result(items.emplace(name, Group::ptr()));
Group::ptr& group(result.first->second);
if (result.second)

View File

@@ -6,6 +6,7 @@
#include <xrpl/tx/wasm/WasmCommon.h>
#include <xrpl/tx/wasm/WasmImportsHelper.h>
#include <chrono>
#include <cstdint>
#include <expected>
#include <string>
@@ -127,9 +128,19 @@ runEscrowWasm(
auto& vm = WasmEngine::instance();
// vm.initMaxPages(MAX_PAGES);
auto const start = std::chrono::steady_clock::now();
auto const ret =
vm.run(wasmCode, hfs, gasLimit, funcName, params, createWasmImport(hfs), hfs.getJournal());
// microseconds is intentional. The resolution of the StatsD is milliseconds,
// but this runs too fast for that.
hfs.executionTimeEvent("runEscrowWasm_us")
.notify(
std::chrono::milliseconds{std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - start)
.count()});
if (!ret)
{
#ifdef DEBUG_OUTPUT
@@ -158,9 +169,19 @@ preflightEscrowWasm(
auto& vm = WasmEngine::instance();
// vm.initMaxPages(MAX_PAGES);
auto const start = std::chrono::steady_clock::now();
auto const ret =
vm.check(wasmCode, hfs, funcName, params, createWasmImport(hfs), hfs.getJournal());
// microseconds is intentional. The resolution of the StatsD is milliseconds,
// but this runs too fast for that.
hfs.executionTimeEvent("preflightEscrowWasm_us")
.notify(
std::chrono::milliseconds{std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - start)
.count()});
return ret;
}

View File

@@ -5,6 +5,8 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/insight/Event.h>
#include <xrpl/beast/insight/EventImpl.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/detail/ApplyViewBase.h>
#include <xrpl/protocol/AccountID.h>
@@ -19,14 +21,76 @@
#include <boost/algorithm/hex.hpp>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <expected>
#include <iostream>
#include <iterator>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
namespace xrpl::test {
/**
* Lets a test assert that the WASM execution-timing path fired and inspect the
* recorded durations, without needing a StatsD sink or a full Collector.
*/
struct RecordingEventImpl : public beast::insight::EventImpl
{
std::size_t count{};
value_type last{};
value_type total{};
value_type min{value_type::max()};
value_type max{value_type::min()};
std::vector<value_type> samples;
bool printOnDestruction{};
~RecordingEventImpl() override
{
if (printOnDestruction && count > 0)
{
std::cout << "Mean (us): " << meanUs() << "\n";
}
}
void
notify(value_type const& value) override
{
++count;
last = value;
total += value;
min = std::min(min, value);
max = std::max(max, value);
samples.push_back(value);
}
[[nodiscard]] double
meanUs() const
{
return count != 0 ? static_cast<double>(total.count()) / static_cast<double>(count) : 0.0;
}
[[nodiscard]] value_type
percentile(double p) const
{
if (samples.empty())
{
return value_type{};
}
auto sorted = samples;
std::ranges::sort(sorted);
auto rank = static_cast<std::size_t>((p / 100.0) * static_cast<double>(sorted.size()));
if (rank >= sorted.size())
{
rank = sorted.size() - 1;
}
return sorted[rank];
}
};
class TestLedgerDataProvider : public HostFunctions
{
jtx::Env& env_;
@@ -49,6 +113,7 @@ protected:
test::jtx::Env& env_;
AccountID accountID_;
Bytes data_;
std::shared_ptr<RecordingEventImpl> execTimeEvent_ = std::make_shared<RecordingEventImpl>();
public:
TestHostFunctions(test::jtx::Env& env) : HostFunctions(env.journal), env_(env)
@@ -58,6 +123,21 @@ public:
data_ = Bytes{t.begin(), t.end()};
}
// Return an Event backed by our recording impl so a test can assert that
// the WASM execution was timed. The name is ignored -- every call records
// into the same impl.
[[nodiscard]] beast::insight::Event
executionTimeEvent(std::string_view name) const override
{
return beast::insight::Event(execTimeEvent_);
}
[[nodiscard]] std::shared_ptr<RecordingEventImpl>&
getExecutionTimeEventImpl()
{
return execTimeEvent_;
}
[[nodiscard]] std::expected<std::uint32_t, HostFunctionError>
getLedgerSqn() const override
{

View File

@@ -0,0 +1,95 @@
#include <test/app/wasm_fixtures/fixtures.h>
#include <test/jtx/Env.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/tx/wasm/HostFuncWrapper.h> // IWYU pragma: keep
#include <xrpl/tx/wasm/WasmVM.h>
#include <boost/algorithm/hex.hpp>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#ifdef _DEBUG
// #define DEBUG_OUTPUT 1
#endif
#include <test/app/TestHostFunctions.h>
namespace xrpl::test {
namespace {
std::vector<uint8_t>
hexToBytes(std::string const& hex)
{
auto const ws = boost::algorithm::unhex(hex);
return Bytes(ws.begin(), ws.end());
}
} // namespace
struct WasmPerf_test : public beast::unit_test::Suite
{
template <typename Functor>
void
perf(TestHostFunctions& hfs, size_t runs, Functor&& f)
{
for (auto i = size_t{}; i < runs; ++i)
{
auto result = f();
BEAST_EXPECT(result);
}
BEAST_EXPECT(hfs.getExecutionTimeEventImpl()->count == runs);
}
void
perfEscrowFinish()
{
testcase("perf escrow finish");
using namespace test::jtx;
static constexpr auto kRuns = 1000;
auto const wasm = hexToBytes(kAllHostFunctionsWasmHex);
Env env{*this};
auto hfns = TestHostFunctions{env};
hfns.getExecutionTimeEventImpl()->printOnDestruction = true;
perf(hfns, kRuns, [&] {
return runEscrowWasm(wasm, hfns, 1'000'000, escrowFunctionName, {}).has_value();
});
}
void
perfEscrowCreate()
{
testcase("perf escrow create");
using namespace test::jtx;
static constexpr auto kRuns = 1000;
auto const wasm = hexToBytes(kAllHostFunctionsWasmHex);
Env env{*this};
auto hfns = TestHostFunctions{env};
hfns.getExecutionTimeEventImpl()->printOnDestruction = true;
perf(hfns, kRuns, [&] { return !preflightEscrowWasm(wasm, hfns, escrowFunctionName); });
}
void
run() override
{
using namespace test::jtx;
perfEscrowFinish();
perfEscrowCreate();
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(WasmPerf, app, xrpl);
} // namespace xrpl::test

View File

@@ -47,12 +47,14 @@ add(HostFunctions&, wasm_val_vec_t const* params, wasm_val_vec_t* results)
return nullptr;
}
namespace {
std::vector<uint8_t>
hexToBytes(std::string const& hex)
{
auto const ws = boost::algorithm::unhex(hex);
return Bytes(ws.begin(), ws.end());
}
} // namespace
template <class IT, class T>
unsigned

View File

@@ -14,7 +14,6 @@
#include <xrpld/app/ledger/OrderBookDBImpl.h>
#include <xrpld/app/ledger/TransactionMaster.h>
#include <xrpld/app/main/BasicApp.h>
#include <xrpld/app/main/CollectorManager.h>
#include <xrpld/app/main/GRPCServer.h>
#include <xrpld/app/main/LoadManager.h>
#include <xrpld/app/main/NodeIdentity.h>
@@ -57,6 +56,7 @@
#include <xrpl/config/BasicConfig.h>
#include <xrpl/config/Constants.h>
#include <xrpl/core/ClosureCounter.h>
#include <xrpl/core/CollectorManager.h>
#include <xrpl/core/HashRouter.h>
#include <xrpl/core/Job.h>
#include <xrpl/core/NetworkIDService.h>

View File

@@ -1,4 +1,5 @@
#include <xrpld/app/main/CollectorManager.h>
#include <xrpl/core/CollectorManager.h>
#include <xrpl/beast/insight/Collector.h>
#include <xrpl/beast/insight/Group.h>

View File

@@ -1,7 +1,6 @@
#pragma once
#include <xrpld/app/main/Application.h>
#include <xrpld/app/main/CollectorManager.h>
#include <xrpld/core/Config.h>
#include <xrpld/rpc/detail/WSInfoSub.h>
@@ -9,6 +8,7 @@
#include <xrpl/beast/insight/Event.h>
#include <xrpl/beast/net/IPEndpoint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/CollectorManager.h>
#include <xrpl/core/JobQueue.h>
#include <xrpl/json/Output.h>
#include <xrpl/resource/ResourceManager.h>

View File

@@ -4,13 +4,13 @@
#include <xrpld/app/ledger/InboundLedgers.h>
#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpld/app/main/Application.h>
#include <xrpld/app/main/CollectorManager.h>
#include <xrpld/app/main/Tuning.h>
#include <xrpld/core/Config.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/core/CollectorManager.h>
#include <xrpl/shamap/FullBelowCache.h>
#include <xrpl/shamap/TreeNodeCache.h>

View File

@@ -1,9 +1,8 @@
#pragma once
#include <xrpld/app/main/CollectorManager.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/CollectorManager.h>
#include <xrpl/nodestore/Database.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/shamap/Family.h>