Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
a698ea2
libfc: add fc::json_writer + fc::to_json_stream streaming JSON emission
heifner Apr 19, 2026
ea33522
libfc: add to_json_stream overloads for time, sha256, variant, varian…
heifner Apr 19, 2026
1298ab2
libfc: add to_json_stream for fc::crypto::public_key and fc::crypto::…
heifner Apr 19, 2026
628cd72
http_plugin + trace_api: wire trace_api/get_block through streaming J…
heifner Apr 19, 2026
015be26
benchmark: add trace_api_json measuring variant vs streaming block se…
heifner Apr 19, 2026
9d0a8e1
fc::json_writer: faster hex and integer emission
heifner Apr 25, 2026
3982f11
trace_api: use json_writer::value_hex for action data + return_value
heifner Apr 25, 2026
7e3970d
bench: variant-only A/B + isolated hex / integer micro-benches
heifner Apr 25, 2026
5bf1f49
fc::json_writer: add set / set_raw fused emitters
heifner Apr 25, 2026
27edcb8
trace_api: use json_writer set / set_raw chains in process_block_to_json
heifner Apr 25, 2026
08307bf
bench: add struct-pass scenarios for HTTP-thread streaming pivot
heifner Apr 25, 2026
38f1dc3
http_plugin: add streaming-cb registration path + handle_exception_st…
heifner Apr 26, 2026
6d5c390
http_plugin: add CALL_ASYNC_WITH_400_STREAM + CALL_WITH_400_STREAM_PO…
heifner Apr 26, 2026
9787ce5
chain_api_plugin: migrate /v1/chain/get_table_rows to streaming-cb path
heifner Apr 26, 2026
5af6905
libfc: add fc::serialize_as_string trait
heifner Apr 27, 2026
9a9cedc
chain: migrate name/asset/symbol/symbol_code/block_timestamp to FC_SE…
heifner Apr 27, 2026
0f05d65
chain_api_plugin: migrate /v1/chain/get_account to streaming-cb path
heifner Apr 27, 2026
f28d665
libfc: add FC_SERIALIZE_AS_STRING_TEMPLATE for class-template types
heifner Apr 27, 2026
ec9166a
libfc: migrate sha1/sha224/sha256/sha512/sha3/ripemd160/keccak256/bla…
heifner Apr 27, 2026
7249313
libfc: migrate bls public_key/signature/aggregate_signature/private_k…
heifner Apr 27, 2026
4cd3b0d
libfc: remove unused fc::fixed_string
heifner Apr 27, 2026
067ef48
libfc: migrate bitset + url to FC_SERIALIZE_AS_STRING
heifner Apr 27, 2026
b08be85
libfc: extract json yield/escape into json_yield.hpp; co-locate to_js…
heifner Apr 28, 2026
5dc9c26
chain_api_plugin: migrate sync read-only endpoints to streaming-cb path
heifner Apr 28, 2026
a452c0e
libfc: streaming variant walker - to_json_stream(variant, w) emits to…
heifner Apr 28, 2026
357f913
chain_api_plugin: migrate get_block_info / get_block_header_state / g…
heifner Apr 28, 2026
14b1400
abi_serializer: add streaming JSON path (binary_to_json_stream)
heifner Apr 28, 2026
d313512
chain_plugin: stream get_table_rows directly into json_writer
heifner Apr 28, 2026
c432c3f
abi_serializer: parameterise to_variant on Sink; migrate get_block to…
heifner Apr 28, 2026
0e45e98
chain: make next_function a copyable wrapper over std::move_only_func…
heifner Apr 28, 2026
b2bf4fa
chain_api_plugin: migrate remaining variant-cb endpoints to streaming
heifner Apr 28, 2026
f2d238d
producer_api_plugin: migrate variant-cb endpoints to streaming
heifner Apr 28, 2026
dd19ffc
net_api_plugin: migrate variant-cb endpoints to streaming
heifner Apr 28, 2026
65c3ec7
db_size_api_plugin: migrate variant-cb endpoint to streaming
heifner Apr 28, 2026
0f9fb12
http_plugin: drop unused CALL_WITH_400_POST variant-cb macro
heifner Apr 28, 2026
8db0f57
http_plugin: replace streaming-cb macros with bind_stream<MethodPtr, …
heifner Apr 29, 2026
afaa4cc
libfc: add to_variant<bool> overload + fix map_object_emission expect…
heifner Apr 29, 2026
174f80f
libfc-test: locate source root when this checkout is a git worktree
heifner Apr 29, 2026
57fff0f
chain_plugin: defer abi parsing in get_block_stream off the read-only…
heifner Apr 29, 2026
a2487db
chain_plugin / http_plugin: fix misleading "Hop 2" comments -- asio d…
heifner Apr 29, 2026
e65ce39
chain_plugin: move get_raw_block off the read-only queue via dispatch…
heifner Apr 29, 2026
be01f11
chain_plugin: get_block_info async + typed result; defer ROA abi pars…
heifner Apr 29, 2026
48d536f
benchmark: add abi_serializer scenarios to the unified benchmark harness
heifner Apr 29, 2026
673f34d
chain_plugin: replace fc::variant fields in get_block_header / get_fi…
heifner Apr 29, 2026
d5ec7e2
libfc: streaming overloads for fc::blob, fc::exception, fc::log_messa…
heifner Apr 29, 2026
e661cd5
chain_plugin: trx_retry stores raw ABIs + trace; defer ABI parse and …
heifner Apr 29, 2026
90d02be
Merge branch 'feature/fc-variant-perf' into feature/fc-json-stream
heifner Apr 30, 2026
ea8c876
Merge branch 'refactor/next-function-move-only' into feature/fc-json-…
heifner Apr 30, 2026
7e149ed
trace_api: fix status enum + block_time JSON shape regressions
heifner Apr 30, 2026
a401e7f
chain_api_plugin: restore /v1/chain/get_info to api_category::node
heifner Apr 30, 2026
de584b3
fc::bitset: restore MAX_NUM_ARRAY_ELEMENTS cap on to_string
heifner Apr 30, 2026
0f429b4
fc::crypto: =delete to_json_stream for private keys
heifner Apr 30, 2026
bb66b8d
libfc: streaming JSON correctness + small cleanups
heifner Apr 30, 2026
7e2cfd2
chain: abi_serializer mid-stream throw rollback + cleanups
heifner Apr 30, 2026
a2103b6
chain_plugin + http_plugin: PR-review followups
heifner Apr 30, 2026
a1a89c2
Merge branch 'feature/fc-variant-perf' into feature/fc-json-stream
heifner Apr 30, 2026
c18d935
Merge branch 'feature/fc-variant-perf' into feature/fc-json-stream
heifner Apr 30, 2026
cc0e85a
libfc: to_json_stream(double/float) emits variant-equivalent quoted f…
heifner Apr 30, 2026
45a524b
Merge branch 'feature/fc-variant-perf' into feature/fc-json-stream
heifner Apr 30, 2026
f904fe4
abi_serializer: inline restructuring summary into add_specialized_str…
heifner Apr 30, 2026
e4b19bd
libfc: rename json_yield.hpp -> json_escape.hpp (escape_string is the…
heifner Apr 30, 2026
bb05817
trace_api: migrate get_transaction_trace to streaming JSON
heifner May 6, 2026
1d737c8
trace_api: stream params/return_data via abi_serializer::binary_to_js…
heifner May 6, 2026
df8318c
Merge remote-tracking branch 'origin/master' into feature/fc-json-stream
heifner Jul 13, 2026
e559105
fix streaming http fallback, status codes, and request-body accounting
heifner Jul 13, 2026
8a49a3e
apply pre-pr review cleanups to the streaming json feature
heifner Jul 13, 2026
493c9a4
fix macOS build and a flaky streaming get_table_rows test
heifner Jul 14, 2026
166c767
add platform-specific integer to_json_stream overloads for macOS
heifner Jul 14, 2026
f051c02
fix value_double_locale_independent for macOS libc++
heifner Jul 14, 2026
1c64dcd
Merge remote-tracking branch 'origin/master' into feature/fc-json-stream
heifner Jul 20, 2026
6f5acf3
http: charge streaming response emission against bytes_in_flight incr…
heifner Jul 20, 2026
b53438b
fc: enforce the response budget within single JSON tokens
heifner Jul 20, 2026
9d3b330
fc: derive guarded emission chunking from the writer's stride
heifner Jul 20, 2026
db98634
fc: approve a string token's size with the guard before escape_string…
heifner Jul 23, 2026
a3c4562
http: reserve queued streaming payloads against bytes_in_flight
heifner Jul 23, 2026
cd02577
trace_api: emit trace responses through the guarded streaming writer
heifner Jul 23, 2026
0464954
fc: preserve NaN sign in streaming double emission
heifner Jul 23, 2026
e00f963
fc: format non-finite doubles sign-aware in the variant path
heifner Jul 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
file(GLOB BENCHMARK CONFIGURE_DEPENDS "*.cpp")
add_executable( benchmark ${BENCHMARK} )

target_link_libraries( benchmark sysio_testing fc Boost::program_options bn256::bn256)
# trace_api_plugin brings the process_block / process_block_to_json implementations
# used by the trace_api_json benchmark; its include path pulls in the data_log_entry
# variant type needed by the synthetic trace builder.
target_link_libraries( benchmark sysio_testing fc Boost::program_options bn256::bn256 trace_api_plugin)
target_include_directories( benchmark PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}/../unittests/include"
)

# ABI fixture paths consumed by abi_serializer.cpp. These point at the system-contract
# sources, not the built artifacts, so the benchmark works regardless of build state.
target_compile_definitions( benchmark
PRIVATE
TOKEN_ABI_PATH="${CMAKE_SOURCE_DIR}/contracts/sysio.token/sysio.token.abi"
SYSTEM_ABI_PATH="${CMAKE_SOURCE_DIR}/contracts/sysio.system/sysio.system.abi"
)

# Standalone dedup head-to-head benchmark. Links only sysio_chain + chainbase (no testing
# harness / contracts), so it builds without the system-contract WASMs. Not built by default.
add_executable( dedup_bench EXCLUDE_FROM_ALL dedup/dedup_bench.cpp )
Expand Down
344 changes: 344 additions & 0 deletions benchmark/abi_serializer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,344 @@
#include <benchmark.hpp>

#include <sysio/chain/abi_def.hpp>
#include <sysio/chain/abi_serializer.hpp>
#include <sysio/chain/action.hpp>
#include <sysio/chain/asset.hpp>
#include <sysio/chain/block.hpp>
#include <sysio/chain/transaction.hpp>

#include <fc/io/json.hpp>
#include <fc/io/json_stream.hpp>
#include <fc/reflect/json_stream.hpp>
#include <fc/variant.hpp>

#include <filesystem>
#include <fstream>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>

// Microbenchmark for sysio::chain::abi_serializer.
//
// Measures construction cost and steady-state decode throughput so that
// successive optimization commits can report a verifiable before/after
// delta. NOT a correctness test -- abi_tests.cpp covers that.
//
// Run via the unified benchmark harness:
//
// ninja -C cmake-build-release -j8 benchmark
// ./cmake-build-release/benchmark/benchmark abi_serializer --num-runs 10
//
// Release build (`-DCMAKE_BUILD_TYPE=Release`, `-O3`) is required for
// comparable numbers; debug / RelWithDebInfo timings are dominated by
// instrumentation. Each scenario does its own inner loop sized to keep
// per-call overhead amortised; the harness's `--num-runs` controls how
// many times the outer scenario is repeated for variance reporting.

namespace sysio::benchmark {

using namespace sysio::chain;
using bytes = sysio::benchmark::bytes;

namespace {

constexpr fc::microseconds max_serialization_time = fc::microseconds{1'000'000};

abi_serializer::yield_function_t yield_fn() {
return abi_serializer::create_yield_function(max_serialization_time);
}

std::string slurp(const std::filesystem::path& p) {
std::ifstream f(p);
if (!f) {
std::cerr << "abi_serializer benchmark: failed to open " << p << "\n";
std::exit(1);
}
std::stringstream ss;
ss << f.rdbuf();
return ss.str();
}

} // namespace

void abi_serializer_benchmarking() {
const std::filesystem::path token_path = TOKEN_ABI_PATH;
const std::filesystem::path system_path = SYSTEM_ABI_PATH;

const auto token_abi = fc::json::from_string(slurp(token_path)).as<abi_def>();
const auto system_abi = fc::json::from_string(slurp(system_path)).as<abi_def>();

// Prebuild serializers + packed payloads for the steady-state decode
// scenarios. Cost of these setup steps is NOT in the measured region.
abi_serializer token_abis{abi_def(token_abi), yield_fn()};
abi_serializer system_abis{abi_def(system_abi), yield_fn()};

const auto transfer_var = fc::json::from_string(
R"({"from":"alice","to":"bob","quantity":"1.0000 SYS","memo":"benchmark payment"})");
chain::bytes transfer_bytes;
try {
transfer_bytes = token_abis.variant_to_binary("transfer", transfer_var, yield_fn());
} catch (const std::exception& e) {
std::cerr << "abi_serializer benchmark: transfer pack failed: " << e.what() << "\n";
return;
}

// newaccount exercises a nested struct path (authority sub-struct + array
// of key_weights). An authority with one key is enough to cover the
// nested+array code paths without bloating the fixture.
const auto newaccount_var = fc::json::from_string(
R"({"creator":"sysio","name":"alice","owner":)"
R"({"threshold":1,"keys":[{"key":"SYS1111111111111111111111111111111114T1Anm","weight":1}],)"
R"("accounts":[],"waits":[]},"active":)"
R"({"threshold":1,"keys":[{"key":"SYS1111111111111111111111111111111114T1Anm","weight":1}],)"
R"("accounts":[],"waits":[]}})");
chain::bytes newaccount_bytes;
try {
newaccount_bytes = system_abis.variant_to_binary("newaccount", newaccount_var, yield_fn());
} catch (const std::exception& e) {
std::cerr << "abi_serializer benchmark: newaccount pack failed: " << e.what() << "\n";
return;
}

// regproducer -- flat-struct baseline comparable to transfer but in a
// larger ABI context. Measures per-field dispatch amortised over the
// whole-action cost in the big-ABI case.
chain::bytes regproducer_bytes;
bool regproducer_ok = false;
try {
const auto regproducer_var = fc::json::from_string(
R"({"producer":"alice","producer_key":"SYS1111111111111111111111111111111114T1Anm",)"
R"("url":"https://example.com","location":0})");
regproducer_bytes = system_abis.variant_to_binary("regproducer", regproducer_var, yield_fn());
regproducer_ok = true;
} catch (const std::exception& e) {
std::cerr << "abi_serializer benchmark warning: regproducer prepack failed (" << e.what()
<< "); skipping regproducer scenarios.\n";
}

// Construction scenarios. Each construction is slow enough that one outer
// run is meaningful; no inner loop needed.
benchmarking("abi_serializer construct token (small ~4KB)", [&] {
abi_serializer a{abi_def(token_abi), yield_fn()};
(void)a;
});
benchmarking("abi_serializer construct system (~37KB)", [&] {
abi_serializer a{abi_def(system_abi), yield_fn()};
(void)a;
});

// Steady-state decode (variant path). Inner loop sized to keep per-op
// overhead well above timer resolution.
constexpr size_t decode_iters = 10000;
constexpr size_t big_decode_iters = 5000;

benchmarking("decode transfer x10000 (variant)", [&] {
for (size_t i = 0; i < decode_iters; ++i) {
auto v = token_abis.binary_to_variant("transfer", transfer_bytes, yield_fn());
(void)v;
}
});
benchmarking("decode newaccount x5000 (variant, nested)", [&] {
for (size_t i = 0; i < big_decode_iters; ++i) {
auto v = system_abis.binary_to_variant("newaccount", newaccount_bytes, yield_fn());
(void)v;
}
});
if (regproducer_ok) {
benchmarking("decode regproducer x5000 (variant)", [&] {
for (size_t i = 0; i < big_decode_iters; ++i) {
auto v = system_abis.binary_to_variant("regproducer", regproducer_bytes, yield_fn());
(void)v;
}
});
}

// Round-trip pack+unpack of transfer -- covers the pack half, not just unpack.
benchmarking("roundtrip transfer x5000 (pack+unpack)", [&] {
for (size_t i = 0; i < big_decode_iters; ++i) {
auto bin = token_abis.variant_to_binary("transfer", transfer_var, yield_fn());
auto var = token_abis.binary_to_variant("transfer", bin, yield_fn());
(void)var;
}
});

// resolve_type on a typedef ("account_name" -> "name") -- typedef chain walk.
benchmarking("resolve_type account_name x100000", [&] {
const std::string probe = "account_name";
for (size_t i = 0; i < 100000; ++i) {
auto r = system_abis.resolve_type(probe);
(void)r;
}
});

// -------------------------------------------------------------------------
// Streaming JSON comparisons. Each pair measures the same payload twice:
// - "(variant) json": binary_to_variant + fc::json::to_string
// - "(stream) json" : binary_to_json_stream directly into a json_writer
//
// The streaming path skips the per-field fc::variant tree and the post-pass
// string serialiser, so the gap should grow with field count.
// -------------------------------------------------------------------------

benchmarking("transfer x10000 (variant) json", [&] {
for (size_t i = 0; i < decode_iters; ++i) {
auto v = token_abis.binary_to_variant("transfer", transfer_bytes, yield_fn());
auto s = fc::json::to_string(v, fc::time_point::maximum());
(void)s;
}
});
benchmarking("transfer x10000 (stream) json", [&] {
for (size_t i = 0; i < decode_iters; ++i) {
std::string out;
out.reserve(256);
fc::json_writer w(out);
token_abis.binary_to_json_stream("transfer", transfer_bytes, w, yield_fn());
}
});

benchmarking("newaccount x5000 (variant) json", [&] {
for (size_t i = 0; i < big_decode_iters; ++i) {
auto v = system_abis.binary_to_variant("newaccount", newaccount_bytes, yield_fn());
auto s = fc::json::to_string(v, fc::time_point::maximum());
(void)s;
}
});
benchmarking("newaccount x5000 (stream) json", [&] {
for (size_t i = 0; i < big_decode_iters; ++i) {
std::string out;
out.reserve(512);
fc::json_writer w(out);
system_abis.binary_to_json_stream("newaccount", newaccount_bytes, w, yield_fn());
}
});

if (regproducer_ok) {
benchmarking("regproducer x5000 (variant) json", [&] {
for (size_t i = 0; i < big_decode_iters; ++i) {
auto v = system_abis.binary_to_variant("regproducer", regproducer_bytes, yield_fn());
auto s = fc::json::to_string(v, fc::time_point::maximum());
(void)s;
}
});
benchmarking("regproducer x5000 (stream) json", [&] {
for (size_t i = 0; i < big_decode_iters; ++i) {
std::string out;
out.reserve(256);
fc::json_writer w(out);
system_abis.binary_to_json_stream("regproducer", regproducer_bytes, w, yield_fn());
}
});
}

// -------------------------------------------------------------------------
// get_block-shape comparison. Builds a synthetic signed_block holding a
// single transaction with N transfer actions, then times two paths:
// - variant: abi_serializer::to_variant<signed_block> + json::to_string
// (mirrors chain_plugin::convert_block)
// - stream: abi_serializer::to_json_stream<signed_block> straight into
// a json_writer (mirrors convert_block_stream)
// The actions count surfaces the per-action ABI decode cost, which is the
// variant path's main allocation hot spot.
// -------------------------------------------------------------------------

constexpr size_t actions_per_trx = 4;
constexpr size_t block_iters = 2000;

transaction inner_trx;
inner_trx.expiration = fc::time_point_sec{ fc::time_point::now() + fc::seconds(60) };
inner_trx.ref_block_num = 1;
inner_trx.ref_block_prefix = 1;
for (size_t i = 0; i < actions_per_trx; ++i) {
action act;
act.account = "sysio.token"_n;
act.name = "transfer"_n;
act.authorization = { { "alice"_n, "active"_n } };
act.data = transfer_bytes;
inner_trx.actions.push_back(std::move(act));
}

signed_transaction signed_trx{ std::move(inner_trx), {}, {} };
packed_transaction ptrx(std::move(signed_trx), packed_transaction::compression_type::none);
transaction_receipt rcpt(std::move(ptrx));

signed_block block;
block.producer = "alice"_n;
block.timestamp = block_timestamp_type{};
block.transactions.push_back(std::move(rcpt));

// Resolver: sysio.token -> the prebuilt token_abis; other accounts -> nullopt
// (matches production chain_plugin behaviour for unknown accounts).
auto block_resolver = [&token_abis](const name& acct) -> std::optional<abi_serializer> {
if (acct == "sysio.token"_n) return token_abis;
return {};
};

benchmarking("block (4 transfers) x2000 (variant) json", [&] {
for (size_t i = 0; i < block_iters; ++i) {
fc::variant pretty;
abi_serializer::to_variant(block, pretty, block_resolver, max_serialization_time);
auto s = fc::json::to_string(pretty, fc::time_point::maximum());
(void)s;
}
});
benchmarking("block (4 transfers) x2000 (stream) json", [&] {
for (size_t i = 0; i < block_iters; ++i) {
std::string out;
out.reserve(2048);
fc::json_writer w(out);
abi_serializer::to_json_stream(block, w, block_resolver, max_serialization_time);
}
});

// -------------------------------------------------------------------------
// get_block-shape comparison INCLUDING per-request resolver build. The
// scenarios above hold the abi_serializer constant across iterations -- that
// measures only the walk + emit cost. In production each /v1/chain/get_block
// request rebuilds the resolver from the captured abi bytes (Phase 2 of
// read_only::get_block_stream_async), so the per-request cost includes
// abi_serializer::to_abi(bytes, abi_def) + abi_serializer ctor for every
// unique account referenced in the block. These scenarios capture that.
//
// Setup: pack the parsed token ABI to raw bytes once (matches what
// capture_abis would produce), then in each iteration parse + construct +
// walk + emit, simulating the full Phase 2 + Phase 3 work for one request.
// -------------------------------------------------------------------------

const chain::bytes token_abi_bytes = fc::raw::pack(token_abi);

constexpr size_t block_full_iters = 500;

benchmarking("get_block-shape x500 (variant + abi build) json", [&] {
for (size_t i = 0; i < block_full_iters; ++i) {
abi_def abi;
abi_serializer::to_abi(token_abi_bytes, abi);
abi_serializer fresh{std::move(abi), yield_fn()};
auto resolver = [&fresh](const name& acct) -> std::optional<abi_serializer> {
if (acct == "sysio.token"_n) return fresh;
return {};
};
fc::variant pretty;
abi_serializer::to_variant(block, pretty, resolver, max_serialization_time);
auto s = fc::json::to_string(pretty, fc::time_point::maximum());
(void)s;
}
});
benchmarking("get_block-shape x500 (stream + abi build) json", [&] {
for (size_t i = 0; i < block_full_iters; ++i) {
abi_def abi;
abi_serializer::to_abi(token_abi_bytes, abi);
abi_serializer fresh{std::move(abi), yield_fn()};
auto resolver = [&fresh](const name& acct) -> std::optional<abi_serializer> {
if (acct == "sysio.token"_n) return fresh;
return {};
};
std::string out;
out.reserve(2048);
fc::json_writer w(out);
abi_serializer::to_json_stream(block, w, resolver, max_serialization_time);
}
});
}

} // namespace sysio::benchmark
15 changes: 14 additions & 1 deletion benchmark/benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ std::map<std::string, std::function<void()>> features {
{ "blake2", blake2_benchmarking },
{ "bls", bls_benchmarking },
{ "merkle", merkle_benchmarking },
{ "trace_api_json", trace_api_json_benchmarking },
{ "abi_serializer", abi_serializer_benchmarking },
{ "auth", auth_benchmarking }
};

Expand Down Expand Up @@ -78,7 +80,18 @@ void benchmarking(const std::string& name, const std::function<void()>& func,

for (auto i = 0U; i < runs; ++i) {
auto start_time = std::chrono::high_resolution_clock::now();
func();
// A throwing scenario (e.g. an abi-serialization deadline tripped by a transient
// machine stall during a long -r run) reports and skips the scenario instead of
// taking the whole benchmark process down with an uncaught exception.
try {
func();
} catch (const std::exception& e) {
std::cout << name << ": SKIPPED after run " << i << " -- " << e.what() << "\n";
return;
} catch (...) {
std::cout << name << ": SKIPPED after run " << i << " -- unknown exception\n";
return;
}
auto end_time = std::chrono::high_resolution_clock::now();

uint64_t duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count();
Expand Down
Loading
Loading