perf: speed up G-code export by 7-26% via typed config apply (#16026)

This commit is contained in:
Kris Austin
2026-09-30 18:58:22 -03:00
committed by GitHub
parent ffbbd62355
commit 9bca3d9f7d
4 changed files with 104 additions and 6 deletions
+5 -1
View File
@@ -2702,6 +2702,9 @@ public:
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
// Collect names of all configuration values maintained by this configuration store.
virtual t_config_option_keys keys() const = 0;
// Set this config's options on target member by member, when target is of this config's static type or
// derives from it, and return true. apply() prefers this to looking every key up by name.
virtual bool apply_to(ConfigBase &/*target*/) const { return false; }
protected:
// Verify whether the opt_key has not been obsoleted or renamed.
@@ -2753,7 +2756,8 @@ public:
// Apply all keys of other ConfigBase defined by this->def() to this ConfigBase.
// An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(),
// or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
void apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->apply_only(other, other.keys(), ignore_nonexistent); }
void apply(const ConfigBase &other, bool ignore_nonexistent = false)
{ if (! other.apply_to(*this)) this->apply_only(other, other.keys(), ignore_nonexistent); }
// Apply explicitely enumerated keys of other ConfigBase defined by this->def() to this ConfigBase.
// An UnknownOptionException is thrown in case some option keys are not defined by this->def(),
// or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
+15
View File
@@ -11958,6 +11958,21 @@ PRINT_CONFIG_CACHE_INITIALIZE((
SLAMaterialConfig, SLAPrintConfig, SLAPrintObjectConfig, SLAPrinterConfig, SLAFullPrintConfig))
static int print_config_static_initialized = print_config_static_initializer();
// The same set() calls ConfigBase::apply_only() makes, without looking every key up by name. Out of line so the
// option list is expanded for this once, not in every file that includes PrintConfig.hpp.
#define PRINT_CONFIG_APPLY_TO_DEFINITION(r, data, CLASS_NAME) \
bool CLASS_NAME::apply_to(ConfigBase &target) const \
{ \
auto *dst = dynamic_cast<CLASS_NAME*>(&target); \
if (dst == nullptr) \
return false; \
visit_option_pairs(*dst, *this, [](const char*, ConfigOption &a, const ConfigOption &b) { a.set(&b); return true; }); \
return true; \
}
BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_APPLY_TO_DEFINITION, _, (PrintObjectConfig)(PrintRegionConfig)(MachineEnvelopeConfig)(GCodeConfig)
(SLAMaterialConfig)(SLAPrintConfig)(SLAPrintObjectConfig)(SLAPrinterConfig))
#undef PRINT_CONFIG_APPLY_TO_DEFINITION
//BBS: remove unused command currently
CLIActionsConfigDef::CLIActionsConfigDef()
{
+14 -5
View File
@@ -1033,9 +1033,10 @@ public: \
#define PRINT_CONFIG_CLASS_ELEMENT_DEFINITION(r, data, elem) BOOST_PP_TUPLE_ELEM(0, elem) BOOST_PP_TUPLE_ELEM(1, elem);
#define PRINT_CONFIG_CLASS_ELEMENT_VISIT(r, data, elem) if (! f(BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)), this->BOOST_PP_TUPLE_ELEM(1, elem), rhs.BOOST_PP_TUPLE_ELEM(1, elem))) return;
#define PRINT_CONFIG_CLASS_ELEMENT_VISIT_SELF(r, data, elem) if (! f(BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)), self.BOOST_PP_TUPLE_ELEM(1, elem), rhs.BOOST_PP_TUPLE_ELEM(1, elem))) return;
// Each option list is expanded into the members and again into for_each_option_pair(), which calls
// f(key, this->option, rhs.option) in declaration order and stops when f returns false. hash(),
// operator==, operator< and initialize() iterate the options through that visitor.
// operator==, operator<, initialize() and apply_to() iterate the options through that visitor.
#define PRINT_CONFIG_CLASS_COMMON_BODY(CLASS_NAME) \
size_t hash() const throw() \
{ \
@@ -1067,11 +1068,16 @@ class CLASS_NAME : public StaticPrintConfig { \
STATIC_PRINT_CONFIG_CACHE(CLASS_NAME) \
public: \
BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_DEFINITION, _, PARAMETER_DEFINITION_SEQ) \
template<typename F> void for_each_option_pair(const CLASS_NAME &rhs, F &&f) const \
{ \
BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_VISIT, _, PARAMETER_DEFINITION_SEQ) \
} \
template<typename F> void for_each_option_pair(const CLASS_NAME &rhs, F &&f) const { visit_option_pairs(*this, rhs, f); } \
/* Defined in PrintConfig.cpp. */ \
bool apply_to(ConfigBase &target) const override; \
PRINT_CONFIG_CLASS_COMMON_BODY(CLASS_NAME) \
private: \
/* The one expansion of the option list, for a const self and for apply_to()'s mutable target. */ \
template<typename Self, typename F> static void visit_option_pairs(Self &self, const CLASS_NAME &rhs, F &&f) \
{ \
BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_VISIT_SELF, _, PARAMETER_DEFINITION_SEQ) \
} \
};
#define PRINT_CONFIG_CLASS_DERIVED_CLASS_LIST_ITEM(r, data, i, elem) BOOST_PP_COMMA_IF(i) public elem
@@ -1092,6 +1098,8 @@ class CLASS_NAME : PRINT_CONFIG_CLASS_DERIVED_CLASS_LIST(CLASSES_PARENTS_TUPLE)
public: \
PARAMETER_DEFINITION \
template<typename F> void for_each_option_pair(const CLASS_NAME &rhs, F &&f) const { PARAMETER_VISIT } \
/* Its parents each apply themselves to a target member by member, so this one keeps the lookup by name. */ \
bool apply_to(ConfigBase &/*target*/) const override { return false; } \
size_t hash() const throw() \
{ \
size_t seed = 0; \
@@ -2182,6 +2190,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE0(
#undef STATIC_PRINT_CONFIG_CACHE_DERIVED
#undef PRINT_CONFIG_CLASS_ELEMENT_DEFINITION
#undef PRINT_CONFIG_CLASS_ELEMENT_VISIT
#undef PRINT_CONFIG_CLASS_ELEMENT_VISIT_SELF
#undef PRINT_CONFIG_CLASS_COMMON_BODY
#undef PRINT_CONFIG_CLASS_DEFINE
#undef PRINT_CONFIG_CLASS_DERIVED_CLASS_LIST
+70
View File
@@ -1289,3 +1289,73 @@ TEST_CASE("Static print configs compare, order and hash by their option values",
REQUIRE(c.optptr("gcode_flavor") == &c.gcode_flavor);
}
}
namespace {
// Keys whose values differ between two full configs, compared as text so enum names count too.
std::vector<std::string> differing_keys(const FullPrintConfig &a, const FullPrintConfig &b)
{
std::vector<std::string> keys;
for (const std::string &key : a.keys())
if (a.opt_serialize(key) != b.opt_serialize(key))
keys.push_back(key);
return keys;
}
// Applies source to one full config member by member and to another key by key, as apply() did before
// static configs could apply themselves.
template<class Source> void check_member_apply_matches_key_apply(const Source &source)
{
FullPrintConfig by_member;
FullPrintConfig by_key;
by_member.apply(source);
by_key.apply_only(source, source.keys());
CHECK(differing_keys(by_member, by_key).empty());
CHECK_FALSE(differing_keys(by_member, FullPrintConfig()).empty());
}
} // namespace
TEST_CASE("A static config applies itself onto a config of its type as a lookup by name would", "[Config]")
{
SECTION("region config")
{
PrintRegionConfig region;
region.sparse_infill_pattern.value = ipGyroid;
region.outer_wall_speed.values = {37.};
region.sparse_infill_density.value = 35.;
FullPrintConfig full;
REQUIRE(region.apply_to(full));
check_member_apply_matches_key_apply(region);
}
SECTION("object config")
{
PrintObjectConfig object;
object.seam_position.value = spRear;
object.wall_generator.value = PerimeterGeneratorType::Arachne;
object.support_speed.values = {33.};
object.enable_support.value = true;
FullPrintConfig full;
REQUIRE(object.apply_to(full));
check_member_apply_matches_key_apply(object);
}
SECTION("G-code config, whose enum lists carry their names through a keys map")
{
GCodeConfig gcode;
gcode.z_hop_types.values = {int(zhtSpiral)};
gcode.retraction_length.values = {1.5};
FullPrintConfig full;
REQUIRE(gcode.apply_to(full));
check_member_apply_matches_key_apply(gcode);
}
}
TEST_CASE("A static config applied onto a config of another type falls back to a lookup by name", "[Config]")
{
PrintRegionConfig region;
region.sparse_infill_pattern.value = ipGyroid;
DynamicPrintConfig dynamic;
REQUIRE_FALSE(region.apply_to(dynamic));
dynamic.apply(region);
CHECK(dynamic.opt_serialize("sparse_infill_pattern") == "gyroid");
}