Skip to content

Commit

Permalink
compile-time improvements - for_each std::tuple -> std::array
Browse files Browse the repository at this point in the history
  * eliminated looping over a tuple and for_each (expensive to instantiate) and to use a straight `std::array<std::string_view, ...> kDefaultTags` instead. The latter can be also evaluated during compile-time.
  * added ccache support (can be disabled)

Signed-off-by: Ralph J. Steinhagen <[email protected]>
  • Loading branch information
RalphSteinhagen authored and wirew0rm committed May 21, 2024
1 parent 4871965 commit 4069232
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
cxx: g++-14
- cc: clang-18,
cxx: clang++-18
cmake_flags: "-DCMAKE_LINKER=/usr/bin/clang-17"
cmake_flags: "-DCMAKE_LINKER=/usr/bin/clang-18"
- cmake_wrapper: emcmake
cc: emcc
cmake_flags: "-DENABLE_COVERAGE=OFF -DCMAKE_CROSSCOMPILING_EMULATOR=${SYSTEM_NODE}"
Expand Down
15 changes: 13 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ if (NOT DEFINED GR_TOPLEVEL_PROJECT)
endif ()
endif ()

# Use ccache if found and enabled
find_program(CCACHE_PROGRAM ccache)
option(USE_CCACHE "Use ccache if available" ON)
if (CCACHE_PROGRAM AND USE_CCACHE)
message(STATUS "ccache found and will be used")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}")
else ()
message(STATUS "ccache will not be used")
endif ()

set(CMAKE_EXT_DEP_WARNING_GUARD "")
if(DISABLE_EXTERNAL_DEPS_WARNINGS) # enable warnings for external dependencies
set(CMAKE_EXT_DEP_WARNING_GUARD SYSTEM)
Expand Down Expand Up @@ -64,8 +75,8 @@ endif()
string(REPLACE ";" " " ALL_COMPILER_FLAGS "${ALL_COMPILER_FLAGS}")

if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # set default C++ STL to Clang's libc++ when using Clang
add_compile_options(-stdlib=libc++ -fcolor-diagnostics)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++")
add_compile_options(-stdlib=libc++ -fcolor-diagnostics -ftime-trace)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++ -ftime-trace")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-fdiagnostics-color=always)
endif()
Expand Down
35 changes: 35 additions & 0 deletions ClangBuildAnalyzer.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# ClangBuildAnalyzer reads ClangBuildAnalyzer.ini file from the working directory
# when invoked, and various aspects of reporting can be configured this way.
# This file example is setup to be exactly like what the defaults are.

# How many of most expensive things are reported?
[counts]

# files that took most time to parse
fileParse = 10
# files that took most time to generate code for
fileCodegen = 10
# functions that took most time to generate code for
function = 30
# header files that were most expensive to include
header = 10
# for each expensive header, this many include paths to it are shown
headerChain = 5
# templates that took longest to instantiate
template = 30


# Minimum times (in ms) for things to be recorded into trace
[minTimes]

# parse/codegen for a file
file = 20

[misc]

# Maximum length of symbol names printed; longer names will get truncated
maxNameLength = 1000

# Only print "root" headers in expensive header report, i.e.
# only headers that are directly included by at least one source file
onlyRootHeaders = true
27 changes: 18 additions & 9 deletions core/include/gnuradio-4.0/Block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1056,24 +1056,24 @@ class Block : public lifecycle::StateMachine<Derived>, //

if (message.cmd == Set) {
if (!message.data.has_value() && !message.data.value().contains("state")) {
throw gr::exception(fmt::format("block {} (aka. {}) cannot set block state w/o 'state' data msg: {}", unique_name, name, message));
throw gr::exception(fmt::format("propertyCallbackLifecycleState - cannot set block state w/o 'state' data msg: {}", message));
}

std::string stateStr;
try {
stateStr = std::get<std::string>(message.data.value().at("state"));
} catch (const std::exception &e) {
throw gr::exception(fmt::format("block {} property {} state conversion throws {}, msg: {}", unique_name, propertyName, e.what(), message));
throw gr::exception(fmt::format("propertyCallbackLifecycleState - state conversion throws {}, msg: {}", e.what(), message));
} catch (...) {
throw gr::exception(fmt::format("block {} property {} state conversion throws unknown exception, msg: {}", unique_name, propertyName, message));
throw gr::exception(fmt::format("propertyCallbackLifecycleState - state conversion throws unknown exception, msg: {}", message));
}
auto state = magic_enum::enum_cast<lifecycle::State>(stateStr);
if (!state.has_value()) {
throw gr::exception(fmt::format("block {} property {} invalid lifecycle::State conversion from {}, msg: {}", unique_name, propertyName, stateStr, message));
throw gr::exception(fmt::format("propertyCallbackLifecycleState - invalid lifecycle::State conversion from {}, msg: {}", stateStr, message));
}
if (auto e = this->changeStateTo(state.value()); !e) {
throw gr::exception(fmt::format("error in state transition block {} property {} - what: {}", //
unique_name, propertyName, e.error().message, e.error().sourceLocation, e.error().errorTime));
throw gr::exception(fmt::format("propertyCallbackLifecycleState - error in state transition - what: {}", //
e.error().message, e.error().sourceLocation, e.error().errorTime));
}
return std::nullopt;
} else if (message.cmd == Get) {
Expand All @@ -1089,7 +1089,7 @@ class Block : public lifecycle::StateMachine<Derived>, //
return std::nullopt;
}

throw gr::exception(fmt::format("block {} property {} does not implement command {}, msg: {}", unique_name, propertyName, message.cmd, message));
throw gr::exception(fmt::format("propertyCallbackLifecycleState - does not implement command {}, msg: {}", message.cmd, message));
}

std::optional<Message>
Expand Down Expand Up @@ -1125,6 +1125,16 @@ class Block : public lifecycle::StateMachine<Derived>, //
propertyCallbackStagedSettings(std::string_view propertyName, Message message) {
using enum gr::message::Command;
assert(propertyName == block::property::kStagedSetting);
const auto keys = [](const property_map &map) noexcept {
std::string result;
for (const auto &pair : map) {
if (!result.empty()) {
result += ", ";
}
result += pair.first;
}
return result;
};

if (message.cmd == Set) {
if (!message.data.has_value()) {
Expand All @@ -1143,8 +1153,7 @@ class Block : public lifecycle::StateMachine<Derived>, //
return std::nullopt;
}

const auto keys = [](const auto &map) { return fmt::join(map | std::views::transform([](const auto &pair) { return pair.first; }), ", "); };
throw gr::exception(fmt::format("block {} (aka. {}) could not set fields: {}\nvs. available: {}", unique_name, name, keys(notSet), keys(settings().get())));
throw gr::exception(fmt::format("propertyCallbackStagedSettings - could not set fields: {}\nvs. available: {}", keys(notSet), keys(settings().get())));
} else if (message.cmd == Get) {
message.data = self().settings().stagedParameters();
return message;
Expand Down
12 changes: 4 additions & 8 deletions core/include/gnuradio-4.0/Settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,15 +306,11 @@ class BasicSettings : public SettingsBase {
_block->meta_information.value[memberName + "::visible"] = RawType::visible();
}

// detect whether field has one of the DEFAULT_TAGS signature
// detect whether field has one of the kDefaultTags signature
if constexpr (traits::port::is_not_any_port_or_collection<Type> && !std::is_const_v<Type> && is_writable(member) && settings::isSupportedType<Type>()) {
meta::tuple_for_each(
[&memberName, this](auto &&default_tag) {
if (default_tag.shortKey() == memberName) {
_auto_forward.emplace(memberName);
}
},
gr::tag::DEFAULT_TAGS);
if constexpr (std::ranges::find(gr::tag::kDefaultTags, std::string_view(get_display_name_const(member).c_str())) != gr::tag::kDefaultTags.cend()) {
_auto_forward.emplace(memberName);
}
_auto_update.emplace(memberName);
}
};
Expand Down
9 changes: 5 additions & 4 deletions core/include/gnuradio-4.0/Tag.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ namespace gr {
* ```
*/
enum class TagPropagationPolicy {
TPP_DONT = 0, /*!< Scheduler doesn't propagate tags from in- to output. The block itself is free to insert tags. */
TPP_DONT = 0, /*!< Scheduler doesn't propagate tags from in- to output. The block itself is free to insert tags. */
TPP_ALL_TO_ALL = 1, /*!< Propagate tags from all in- to all outputs. The scheduler takes care of that. */
TPP_ONE_TO_ONE = 2, /*!< Propagate tags from n. input to n. output. Requires same number of in- and outputs */
TPP_CUSTOM = 3 /*!< Like TPP_DONT, but signals the block it should implement application-specific forwarding behaviour. */
TPP_CUSTOM = 3 /*!< Like TPP_DONT, but signals the block it should implement application-specific forwarding behaviour. */
};

using property_map = pmtv::map_t;
Expand Down Expand Up @@ -246,8 +246,9 @@ inline EM_CONSTEXPR_STATIC DefaultTag<"reset_default", bool, "", "reset block st
inline EM_CONSTEXPR_STATIC DefaultTag<"store_default", bool, "", "store block settings as default"> STORE_DEFAULTS;
inline EM_CONSTEXPR_STATIC DefaultTag<"end_of_stream", bool, "", "end of stream, receiver should change to DONE state"> END_OF_STREAM;

inline constexpr std::tuple DEFAULT_TAGS = { SAMPLE_RATE, SIGNAL_NAME, SIGNAL_UNIT, SIGNAL_MIN, SIGNAL_MAX, TRIGGER_NAME, TRIGGER_TIME,
TRIGGER_OFFSET, TRIGGER_META_INFO, CONTEXT, RESET_DEFAULTS, STORE_DEFAULTS, END_OF_STREAM };
inline constexpr std::array<std::string_view, 14> kDefaultTags = { "sample_rate", "signal_name", "signal_quantity", "signal_unit", "signal_min", "signal_max", "trigger_name",
"trigger_time", "trigger_offset", "trigger_meta_info", "context", "reset_default", "store_default", "end_of_stream" };

} // namespace tag

} // namespace gr
Expand Down
12 changes: 4 additions & 8 deletions core/include/gnuradio-4.0/Transactions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,11 @@ class CtxSettings : public SettingsBase {
_block->meta_information.value[memberName + "::visible"] = RawType::visible();
}

// detect whether field has one of the DEFAULT_TAGS signature
// detect whether field has one of the kDefaultTags signature
if constexpr (traits::port::is_not_any_port_or_collection<Type> && !std::is_const_v<Type> && is_writable(member) && settings::isSupportedType<Type>()) {
meta::tuple_for_each(
[&memberName, this](auto &&default_tag) {
if (default_tag.shortKey() == memberName) {
_auto_forward.emplace(memberName);
}
},
gr::tag::DEFAULT_TAGS);
if constexpr (std::ranges::find(gr::tag::kDefaultTags, std::string_view(get_display_name_const(member).c_str())) != gr::tag::kDefaultTags.cend()) {
_auto_forward.emplace(memberName);
}
_auto_update.emplace(memberName);
}
};
Expand Down
2 changes: 1 addition & 1 deletion core/test/qa_Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ const boost::ut::suite SettingsTests = [] {
expect(eq(block1.settings().autoUpdateParameters().size(), 8UL));
expect(eq(block1.settings().autoForwardParameters().size(), 2UL));
// need to add 'n_samples_max' to forwarding list for the block to automatically forward it
// as the 'n_samples_max' tag is not part of the canonical 'gr::tag::DEFAULT_TAGS' list
// as the 'n_samples_max' tag is not part of the canonical 'gr::tag::kDefaultTags' list
block1.settings().autoForwardParameters().emplace("n_samples_max");
expect(eq(block1.settings().autoForwardParameters().size(), 3UL));
// same check for block2
Expand Down

0 comments on commit 4069232

Please sign in to comment.