Skip to content

Commit

Permalink
reflection-related build-time optimisations
Browse files Browse the repository at this point in the history
... addresses compile-time inefficiencies Settings handling, notably:
- Refining throw statement logic, reducing compile times from the baseline of 4m 16s to 2m 15s.
- Streamlining the Settings constructor, cutting down compile times further to 1m 17s.

Times were analysed on local i5-based machine using Clang's `-ftime-trace` feature and relying on `chrome://tracing/` flame-chart functionality.
  • Loading branch information
RalphSteinhagen authored and wirew0rm committed Mar 19, 2024
1 parent 1f3469c commit fac32ff
Showing 1 changed file with 42 additions and 45 deletions.
87 changes: 42 additions & 45 deletions core/include/gnuradio-4.0/Settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,46 +281,44 @@ class BasicSettings : public SettingsBase {
}

if constexpr (refl::is_reflectable<TBlock>()) {
meta::tuple_for_each(
[this](auto &&default_tag) {
auto iterate_over_member = [&](auto member) {
using RawType = std::remove_cvref_t<decltype(member(*_block))>;
using Type = unwrap_if_wrapped_t<RawType>;
if constexpr (traits::port::is_not_any_port_or_collection<Type> && !std::is_const_v<Type> && is_writable(member) && settings::isSupportedType<Type>()) {
if (default_tag == get_display_name(member)) {
_auto_forward.emplace(get_display_name(member));
}
_auto_update.emplace(get_display_name(member));
}
};
if constexpr (detail::HasBaseType<TBlock>) {
refl::util::for_each(refl::reflect<typename std::remove_cvref_t<TBlock>::base_t>().members, iterate_over_member);
}
refl::util::for_each(refl::reflect<TBlock>().members, iterate_over_member);
},
gr::tag::DEFAULT_TAGS);
// register block-global description
constexpr bool hasMetaInfo = requires(TBlock t) {
{
unwrap_if_wrapped_t<decltype(t.meta_information)> {}
} -> std::same_as<property_map>;
};

if constexpr (hasMetaInfo && requires(TBlock t) { t.description; }) {
static_assert(std::is_same_v<std::remove_cvref_t<unwrap_if_wrapped_t<decltype(TBlock::description)>>, std::string_view>);
_block->meta_information.value["description"] = std::string(_block->description);
}

// handle meta-information for UI and other non-processing-related purposes
auto iterate_over_member = [&]<typename Member>(Member member) {
using RawType = std::remove_cvref_t<decltype(member(*_block))>;
// disable clang format because v16 cannot handle in-line requires clauses with return types nicely yet
// clang-format off
if constexpr (requires(TBlock t) { t.meta_information; }) {
static_assert(std::is_same_v<unwrap_if_wrapped_t<decltype(_block->meta_information)>, property_map>);
if constexpr (requires(TBlock t) { t.description; }) {
static_assert(std::is_same_v<std::remove_cvref_t<unwrap_if_wrapped_t<decltype(TBlock::description)>>, std::string_view>);
_block->meta_information.value["description"] = std::string(_block->description);
}
auto iterate_over_member = [this]<typename Member>(Member member) {
using RawType = std::remove_cvref_t<decltype(member(*_block))>;
using Type = unwrap_if_wrapped_t<RawType>;
const auto memberName = std::string(get_display_name(member));

if constexpr (hasMetaInfo && AnnotatedType<RawType>) {
_block->meta_information.value[memberName + "::description"] = std::string(RawType::description());
_block->meta_information.value[memberName + "::documentation"] = std::string(RawType::documentation());
_block->meta_information.value[memberName + "::unit"] = std::string(RawType::unit());
_block->meta_information.value[memberName + "::visible"] = RawType::visible();
}

if constexpr (AnnotatedType<RawType>) {
_block->meta_information.value[fmt::format("{}::description", get_display_name(member))] = std::string(RawType::description());
_block->meta_information.value[fmt::format("{}::documentation", get_display_name(member))] = std::string(RawType::documentation());
_block->meta_information.value[fmt::format("{}::unit", get_display_name(member))] = std::string(RawType::unit());
_block->meta_information.value[fmt::format("{}::visible", get_display_name(member))] = RawType::visible();
}
// detect whether field has one of the DEFAULT_TAGS 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 == memberName) {
_auto_forward.emplace(memberName);
}
_auto_update.emplace(memberName);
},
gr::tag::DEFAULT_TAGS);
}
// clang-format on
};

if constexpr (detail::HasBaseType<TBlock>) {
refl::util::for_each(refl::reflect<typename std::remove_cvref_t<TBlock>::base_t>().members, iterate_over_member);
}
Expand Down Expand Up @@ -377,23 +375,22 @@ class BasicSettings : public SettingsBase {
auto iterate_over_member = [&, this](auto member) {
using Type = unwrap_if_wrapped_t<std::remove_cvref_t<decltype(member(*_block))>>;
if constexpr (traits::port::is_not_any_port_or_collection<Type> && !std::is_const_v<Type> && is_writable(member) && settings::isSupportedType<Type>()) {
if (std::string(get_display_name(member)) == key && std::holds_alternative<Type>(value)) {
const auto fieldName = std::string_view(get_display_name(member));
if (fieldName == key && std::holds_alternative<Type>(value)) {
if (_auto_update.contains(key)) {
_auto_update.erase(key);
}
_staged.insert_or_assign(key, value);
SettingsBase::_changed.store(true);
is_set = true;
}
if (std::string(get_display_name(member)) == key && !std::holds_alternative<Type>(value)) {
const std::size_t required_index = meta::to_typelist<pmtv::pmt>::index_of<Type>();
std::visit(
[&key, &value, &required_index](auto &&arg) {
using ActualType = std::decay_t<decltype(arg)>;
throw std::invalid_argument(fmt::format("value for key {} has a wrong type ({}: {}) vs. expected ({}: {})", //
key, value.index(), gr::meta::type_name<ActualType>(), required_index, gr::meta::type_name<Type>()));
},
value);
if (fieldName == key && !std::holds_alternative<Type>(value)) {
throw std::invalid_argument([&key, &value] { // lazy evaluation
const std::size_t actual_index = value.index();
const std::size_t required_index = meta::to_typelist<pmtv::pmt>::index_of<Type>(); // This too, as per your implementation.
return fmt::format("value for key '{}' has a wrong type. Index of actual type: {} ({}), Index of expected type: {} ({})", key, actual_index, "<missing pmt type>",
required_index, gr::meta::type_name<Type>());
}());
}
}
};
Expand Down

0 comments on commit fac32ff

Please sign in to comment.