From 23994c82ae108ff8a5d50939eed42f31fa681872 Mon Sep 17 00:00:00 2001 From: Vladimir Babin Date: Sun, 1 Nov 2026 14:50:05 +0800 Subject: [PATCH 1/2] build: let static-library objects compile without waiting for dependency libraries With the Makefile generator every target waits for the targets it links to be fully archived before any of its own objects start. Static libraries do not link, so this is pure serialization: nothing in graphene_chain starts until fc and protocol finish, and no plugin starts until database.cpp (the longest TU) finishes alone on one core. CMAKE_OPTIMIZE_DEPENDENCIES drops those edges for static libraries while keeping dependencies on custom-command outputs and anything added through add_dependencies. graphene_chain therefore keeps only build_hardfork_hpp as an explicit dependency; the protocol/utilities edges already come from target_link_libraries and were defeating the optimization. The generated hardfork.hpp is no longer listed as a source for the same reason. Cold make -j8 vizd cli_wallet on an 8-core laptop: 805 s -> 305 s wall, user time 1762 s -> 1375 s, i.e. ~2.4 -> ~4.8 cores kept busy. A full make of every target, 13/13 fast ctest and consensus_sim_tests pass on the pruned graph. --- CMakeLists.txt | 7 +++++++ libraries/chain/CMakeLists.txt | 9 ++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 33394953e8..56f588859b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -316,6 +316,13 @@ if(ENABLE_COVERAGE_TESTING) set(CMAKE_CXX_FLAGS "--coverage ${CMAKE_CXX_FLAGS}") endif() +# Static libraries do not link, so a library's objects need not wait for its dependency +# libraries to finish compiling, only for their generated sources. Without this the Makefile +# generator builds target-by-target: nothing in graphene_chain starts until fc/protocol are +# archived, and no plugin starts until database.cpp (the longest TU) finishes on one core. +# Cold `make -j8 vizd cli_wallet` on an 8-core laptop: 805 s -> 305 s wall, ~2.4 -> ~4.8 cores busy. +set(CMAKE_OPTIMIZE_DEPENDENCIES ON) + add_subdirectory(thirdparty) add_subdirectory(libraries) add_subdirectory(plugins) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index f7d9b824f4..d962cab2c1 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -33,9 +33,6 @@ set_source_files_properties("${hardfork_hpp_out}" PROPERTIES GENERATED TRUE) # Include the binary include dir include_directories("${CMAKE_CURRENT_BINARY_DIR}/include") -# Variable used below in add_library -set(hardfork_hpp_file "${hardfork_hpp_out}") - ## SORT .cpp by most likely to change / break compile add_library(graphene_chain ${VIZ_LIBRARY_TYPE} @@ -104,10 +101,12 @@ add_library(graphene_chain ${VIZ_LIBRARY_TYPE} include/graphene/chain/paid_subscription_objects.hpp include/graphene/chain/pm_objects.hpp - ${hardfork_hpp_file} ) -add_dependencies(graphene_chain graphene_protocol graphene_utilities build_hardfork_hpp) +# Only the generated header is a real build-order dependency. Library-to-library order comes +# from target_link_libraries; an explicit add_dependencies here would defeat +# CMAKE_OPTIMIZE_DEPENDENCIES and make every chain TU wait for protocol to finish archiving. +add_dependencies(graphene_chain build_hardfork_hpp) target_link_libraries(graphene_chain graphene_protocol graphene_utilities fc chainbase appbase ${PATCH_MERGE_LIB}) target_include_directories(graphene_chain PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../../") From 006c2158a40b3956cf2bb928dae5ffb25caef5a7 Mon Sep 17 00:00:00 2001 From: Vladimir Babin Date: Sun, 1 Nov 2026 14:50:05 +0800 Subject: [PATCH 2/2] fc: flat static_variant type lookup; test every operation tag round-trips Bumps fc to the non-recursive static_variant position lookup (fc#6): ops.cpp 8.4 s -> 6.7 s, objects byte-identical. Adds an operation_variant consensus_sim suite that, for each of the 107 alternatives, sets the tag, raw-packs and unpacks, converts to and from fc::variant, and copies/moves/assigns, checking which() throughout; pins the tag numbers of vote (0), transfer (2) and validator_equivocation (count-1); and checks that out-of-range tags are rejected both via set_which and via raw unpack. --- tests/consensus_sim/CMakeLists.txt | 1 + .../scenarios/test_operation_variant.cpp | 59 +++++++++++++++++++ thirdparty/fc | 2 +- 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/consensus_sim/scenarios/test_operation_variant.cpp diff --git a/tests/consensus_sim/CMakeLists.txt b/tests/consensus_sim/CMakeLists.txt index e286941967..9472e8d95d 100644 --- a/tests/consensus_sim/CMakeLists.txt +++ b/tests/consensus_sim/CMakeLists.txt @@ -67,6 +67,7 @@ set(SCENARIO_SOURCES scenarios/test_routable_endpoint.cpp scenarios/test_p2p_slot_scale.cpp scenarios/test_db_invariants.cpp + scenarios/test_operation_variant.cpp ) # These scenarios require an actual HF14 activation and never soft-skip. diff --git a/tests/consensus_sim/scenarios/test_operation_variant.cpp b/tests/consensus_sim/scenarios/test_operation_variant.cpp new file mode 100644 index 0000000000..1e07cdc4f3 --- /dev/null +++ b/tests/consensus_sim/scenarios/test_operation_variant.cpp @@ -0,0 +1,59 @@ +// Contract of the `operation` static_variant: every alternative round-trips through the raw +// and JSON serializers under its own tag, tags are what the type list says they are, and an +// out-of-range tag is rejected instead of dispatching into garbage. The variant index is the +// consensus op-id, so this pins the dispatch machinery itself, not any one operation. + +#include + +#include +#include + +using namespace graphene::protocol; + +BOOST_AUTO_TEST_SUITE(operation_variant) + +BOOST_AUTO_TEST_CASE(every_alternative_round_trips_under_its_tag) { + for (int i = 0; i < operation::count(); ++i) { + operation op; + op.set_which(i); + BOOST_REQUIRE_EQUAL(op.which(), i); + + auto bytes = fc::raw::pack(op); + BOOST_CHECK_EQUAL(fc::raw::unpack(bytes).which(), i); + + fc::variant v; + fc::to_variant(op, v); + operation from_json; + fc::from_variant(v, from_json); + BOOST_CHECK_EQUAL(from_json.which(), i); + + operation copied(op); + operation moved(std::move(copied)); + BOOST_CHECK_EQUAL(moved.which(), i); + operation assigned; + assigned = op; + BOOST_CHECK_EQUAL(assigned.which(), i); + } +} + +BOOST_AUTO_TEST_CASE(tags_follow_the_type_list) { + BOOST_CHECK_EQUAL(int(operation::tag::value), 0); + BOOST_CHECK_EQUAL(int(operation::tag::value), 2); + BOOST_CHECK_EQUAL(int(operation::tag::value), operation::count() - 1); + + operation op = transfer_operation(); + BOOST_CHECK_EQUAL(op.which(), int(operation::tag::value)); + op.get().memo = "x"; + BOOST_CHECK_EQUAL(op.get().memo, "x"); + BOOST_CHECK_THROW(op.get(), fc::assert_exception); +} + +BOOST_AUTO_TEST_CASE(out_of_range_tag_is_rejected) { + operation op; + BOOST_CHECK_THROW(op.set_which(operation::count()), fc::exception); + + auto bytes = fc::raw::pack(fc::unsigned_int(operation::count() + 7)); + BOOST_CHECK_THROW(fc::raw::unpack(bytes), fc::exception); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/thirdparty/fc b/thirdparty/fc index 679e89e9cd..7889e2f73a 160000 --- a/thirdparty/fc +++ b/thirdparty/fc @@ -1 +1 @@ -Subproject commit 679e89e9cd503b97b6b1d7e436418408c0f9b35a +Subproject commit 7889e2f73a3918b3855efad69869ec08952a3140