From 921aae23dcf90602a954600a2eeb6e8e788911de Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 13 Jun 2024 14:39:19 +0200 Subject: [PATCH] refactor: `explode` and `get_complexity` compile-time performance improved --- .../mp-units/framework/quantity_spec.h | 227 +++++++++--------- 1 file changed, 109 insertions(+), 118 deletions(-) diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index 958778d4..a40d631e 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -551,17 +551,17 @@ MP_UNITS_EXPORT_BEGIN template [[nodiscard]] consteval QuantitySpec auto operator*(Lhs lhs, Rhs rhs) { - return detail::clone_kind_of( - detail::expr_multiply( - detail::remove_kind(lhs), detail::remove_kind(rhs))); + return detail::clone_kind_of(decltype(detail::expr_multiply( + detail::remove_kind(lhs), detail::remove_kind(rhs))){}); } template [[nodiscard]] consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs) { return detail::clone_kind_of( - detail::expr_divide( - detail::remove_kind(lhs), detail::remove_kind(rhs))); + decltype(detail::expr_divide( + detail::remove_kind(lhs), detail::remove_kind(rhs))){}); } template @@ -570,7 +570,7 @@ template return is_same_v; } -[[nodiscard]] consteval QuantitySpec auto inverse(QuantitySpec auto q) { return dimensionless / q; } +[[nodiscard]] consteval QuantitySpec auto inverse(QuantitySpec auto q) { return decltype(dimensionless / q){}; } /** @@ -592,8 +592,8 @@ template return q; else if constexpr (detail::DerivedQuantitySpec) return detail::clone_kind_of( - detail::expr_pow( - detail::remove_kind(q))); + decltype(detail::expr_pow(detail::remove_kind(q))){}); else if constexpr (Den == 1) return detail::clone_kind_of(derived_quantity_spec>{}); else @@ -608,7 +608,7 @@ template * * @return QuantitySpec The result of computation */ -[[nodiscard]] consteval QuantitySpec auto sqrt(QuantitySpec auto q) { return pow<1, 2>(q); } +[[nodiscard]] consteval QuantitySpec auto sqrt(QuantitySpec auto q) { return decltype(pow<1, 2>(q)){}; } /** @@ -618,44 +618,43 @@ template * * @return QuantitySpec The result of computation */ -[[nodiscard]] consteval QuantitySpec auto cbrt(QuantitySpec auto q) { return pow<1, 3>(q); } +[[nodiscard]] consteval QuantitySpec auto cbrt(QuantitySpec auto q) { return decltype(pow<1, 3>(q)){}; } MP_UNITS_EXPORT_END namespace detail { -enum class specs_convertible_result : std::int8_t { no, cast, explicit_conversion, yes }; - template -[[nodiscard]] consteval int get_complexity(Q); +[[nodiscard]] consteval auto get_complexity(Q); template -[[nodiscard]] consteval int get_complexity(type_list) +[[nodiscard]] consteval auto get_complexity(type_list) { - return (0 + ... + get_complexity(Ts{})); + return std::integral_constant{}; } template -[[nodiscard]] consteval int get_complexity(power) +[[nodiscard]] consteval auto get_complexity(power) { - return get_complexity(Q{}); + return decltype(get_complexity(Q{})){}; } template -[[nodiscard]] consteval int get_complexity(kind_of_) +[[nodiscard]] consteval auto get_complexity(kind_of_) { - return get_complexity(Q{}); + return decltype(get_complexity(Q{})){}; } template -[[nodiscard]] consteval int get_complexity(Q) +[[nodiscard]] consteval auto get_complexity(Q) { if constexpr (detail::DerivedQuantitySpec) - return get_complexity(typename Q::_num_{}) + get_complexity(typename Q::_den_{}); + return std::integral_constant{}; else if constexpr (requires { Q::_equation_; }) - return 1 + get_complexity(Q::_equation_); + return std::integral_constant{}; else - return 1; + return std::integral_constant{}; } // dimension_one is always the last one @@ -672,8 +671,8 @@ template } template + bool rhs_eq = requires { Rhs::_equation_; }, ratio lhs_compl = decltype(get_complexity(Lhs{}))::value, + ratio rhs_compl = decltype(get_complexity(Rhs{}))::value> struct ingredients_less : std::bool_constant<(lhs_compl > rhs_compl) || (lhs_compl == rhs_compl && ingredients_dimension_less(Lhs::dimension, Rhs::dimension)) || @@ -693,25 +692,21 @@ template return true; } -template +enum class specs_convertible_result : std::int8_t { no, cast, explicit_conversion, yes }; + +template struct explode_to_equation_result { - Q equation; - specs_convertible_result result; + static constexpr QuantitySpec auto equation = Q; + static constexpr specs_convertible_result result = Result; }; -#if MP_UNITS_COMP_CLANG - -template -explode_to_equation_result(Q, specs_convertible_result) -> explode_to_equation_result; - -#endif - template requires requires { Q::_equation_; } [[nodiscard]] consteval auto explode_to_equation(Q q) { - return explode_to_equation_result{ - Q::_equation_, defines_equation(q) ? specs_convertible_result::yes : specs_convertible_result::explicit_conversion}; + return explode_to_equation_result{}; } template @@ -719,30 +714,23 @@ template [[nodiscard]] consteval auto explode_to_equation(power) { constexpr ratio exp = power::exponent; - return explode_to_equation_result{ - pow(Q::_equation_), - defines_equation(Q{}) ? specs_convertible_result::yes : specs_convertible_result::explicit_conversion}; + return explode_to_equation_result(Q::_equation_)){}, + (defines_equation(Q{}) ? specs_convertible_result::yes + : specs_convertible_result::explicit_conversion)>{}; } -template +template struct explode_result { - Q quantity; - specs_convertible_result result = specs_convertible_result::yes; + static constexpr QuantitySpec auto quantity = Q; + static constexpr specs_convertible_result result = Result; - template - [[nodiscard]] consteval explode_result common_convertibility_with(explode_to_equation_result res) const + template + [[nodiscard]] static consteval auto common_convertibility_with(explode_to_equation_result res) { - return {quantity, min(result, res.result)}; + return explode_result{}; } }; -#if MP_UNITS_COMP_CLANG - -template -explode_result(Q) -> explode_result; - -#endif - template [[nodiscard]] consteval auto explode(Q q); @@ -752,24 +740,25 @@ template template [[nodiscard]] consteval auto explode(Q, type_list, type_list) { - constexpr auto n = get_complexity(Num{}); - constexpr auto d = get_complexity(Den{}); + constexpr auto n = decltype(get_complexity(Num{}))::value; + constexpr auto d = decltype(get_complexity(Den{}))::value; constexpr auto max_compl = n > d ? n : d; if constexpr (max_compl == Complexity || ((n >= d && !requires { explode_to_equation(Num{}); }) || (n < d && !requires { explode_to_equation(Den{}); }))) - return explode_result{(map_power(Num{}) * ... * map_power(Nums{})) / (map_power(Den{}) * ... * map_power(Dens{}))}; + return explode_result{}; else { if constexpr (n >= d) { - constexpr auto res = explode_to_equation(Num{}); - return explode((res.equation * ... * map_power(Nums{})) / - (map_power(Den{}) * ... * map_power(Dens{}))) - .common_convertibility_with(res); + constexpr auto res = decltype(explode_to_equation(Num{})){}; + return decltype(explode( + decltype(decltype((res.equation * ... * map_power(Nums{}))){} / + decltype((map_power(Den{}) * ... * map_power(Dens{}))){}){}))::common_convertibility_with(res); } else { constexpr auto res = explode_to_equation(Den{}); - return explode((map_power(Num{}) * ... * map_power(Nums{})) / - (res.equation * ... * map_power(Dens{}))) - .common_convertibility_with(res); + return decltype(explode( + decltype(decltype((map_power(Num{}) * ... * map_power(Nums{}))){} / + decltype((res.equation * ... * map_power(Dens{}))){}){}))::common_convertibility_with(res); } } } @@ -777,54 +766,56 @@ template [[nodiscard]] consteval auto explode(Q, type_list, type_list<>) { - constexpr auto n = get_complexity(Num{}); + constexpr auto n = decltype(get_complexity(Num{}))::value; if constexpr (n == Complexity || !requires { explode_to_equation(Num{}); }) - return explode_result{(map_power(Num{}) * ... * map_power(Nums{}))}; + return explode_result{}; else { - constexpr auto res = explode_to_equation(Num{}); - return explode((res.equation * ... * map_power(Nums{}))).common_convertibility_with(res); + constexpr auto res = decltype(explode_to_equation(Num{})){}; + return decltype(explode( + decltype((res.equation * ... * map_power(Nums{}))){}))::common_convertibility_with(res); } } template [[nodiscard]] consteval auto explode(Q, type_list<>, type_list) { - constexpr auto d = get_complexity(Den{}); + constexpr auto d = decltype(get_complexity(Den{}))::value; if constexpr (d == Complexity || !requires { explode_to_equation(Den{}); }) - return explode_result{dimensionless / (map_power(Den{}) * ... * map_power(Dens{}))}; + return explode_result{}; else { - constexpr auto res = explode_to_equation(Den{}); - return explode(dimensionless / (res.equation * ... * map_power(Dens{}))) - .common_convertibility_with(res); + constexpr auto res = decltype(explode_to_equation(Den{})){}; + return decltype(explode( + decltype(dimensionless / + decltype((res.equation * ... * map_power(Dens{}))){}){}))::common_convertibility_with(res); } } template [[nodiscard]] consteval auto explode(Q, type_list<>, type_list<>) { - return explode_result{dimensionless}; + return explode_result{}; } template [[nodiscard]] consteval auto explode(Q q) { - constexpr auto c = get_complexity(Q{}); + constexpr auto c = decltype(get_complexity(Q{}))::value; if constexpr (c > Complexity) - return explode(q, type_list_sort{}, - type_list_sort{}); + return decltype(explode(q, type_list_sort{}, + type_list_sort{})){}; else - return explode_result{q}; + return explode_result{}; } template -[[nodiscard]] consteval auto explode(Q q) +[[nodiscard]] consteval auto explode(Q) { - constexpr auto c = get_complexity(Q{}); + constexpr auto c = decltype(get_complexity(Q{}))::value; if constexpr (c > Complexity && requires { Q::_equation_; }) { - constexpr auto res = explode_to_equation(Q{}); - return explode(res.equation).common_convertibility_with(res); + constexpr auto res = decltype(explode_to_equation(Q{})){}; + return decltype(explode(res.equation))::common_convertibility_with(res); } else - return explode_result{q}; + return explode_result{}; } template extra template [[nodiscard]] consteval auto extract_convertible_quantities(From, To) { - constexpr auto qfrom = map_power(From{}); - constexpr auto qto = map_power(To{}); + constexpr auto qfrom = decltype(map_power(From{})){}; + constexpr auto qto = decltype(map_power(To{})){}; if constexpr (qfrom.dimension == qto.dimension) { if constexpr (is_specialization_of_power && is_specialization_of_power) { constexpr auto cr = common_ratio(From::exponent, To::exponent); @@ -1023,10 +1014,10 @@ template(num_from, den_from, type_list{}, type_list{}); else { - constexpr auto num_from_compl = get_complexity(NumFrom{}); - constexpr auto den_from_compl = get_complexity(DenFrom{}); - constexpr auto num_to_compl = get_complexity(NumTo{}); - constexpr auto den_to_compl = get_complexity(DenTo{}); + constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value; + constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value; + constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value; + constexpr auto den_to_compl = decltype(get_complexity(DenTo{}))::value; constexpr auto max_compl = max({num_from_compl, num_to_compl, den_from_compl, den_to_compl}); if constexpr (max_compl > 1) { if constexpr (num_from_compl == max_compl) { @@ -1070,9 +1061,9 @@ template(num_from, den_from, type_list{}, type_list{}); else { - constexpr auto den_from_compl = get_complexity(DenFrom{}); - constexpr auto num_to_compl = get_complexity(NumTo{}); - constexpr auto den_to_compl = get_complexity(DenTo{}); + constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value; + constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value; + constexpr auto den_to_compl = decltype(get_complexity(DenTo{}))::value; constexpr auto max_compl = max({num_to_compl, den_from_compl, den_to_compl}); if constexpr (max_compl > 1) { if constexpr (den_from_compl == max_compl) { @@ -1109,9 +1100,9 @@ template(num_from, den_from, type_list{}, type_list{}); else { - constexpr auto num_from_compl = get_complexity(NumFrom{}); - constexpr auto num_to_compl = get_complexity(NumTo{}); - constexpr auto den_to_compl = get_complexity(DenTo{}); + constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value; + constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value; + constexpr auto den_to_compl = decltype(get_complexity(DenTo{}))::value; constexpr auto max_compl = max({num_from_compl, num_to_compl, den_to_compl}); if constexpr (max_compl > 1) { if constexpr (num_from_compl == max_compl) { @@ -1149,9 +1140,9 @@ template(type_list{}, type_list{}, num_to, den_to); else { - constexpr auto num_from_compl = get_complexity(NumFrom{}); - constexpr auto den_from_compl = get_complexity(DenFrom{}); - constexpr auto den_to_compl = get_complexity(DenTo{}); + constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value; + constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value; + constexpr auto den_to_compl = decltype(get_complexity(DenTo{}))::value; constexpr auto max_compl = max({num_from_compl, den_from_compl, den_to_compl}); if constexpr (max_compl > 1) { if constexpr (num_from_compl == max_compl) { @@ -1189,9 +1180,9 @@ template(type_list{}, type_list{}, num_to, den_to); else { - constexpr auto num_from_compl = get_complexity(NumFrom{}); - constexpr auto den_from_compl = get_complexity(DenFrom{}); - constexpr auto num_to_compl = get_complexity(NumTo{}); + constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value; + constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value; + constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value; constexpr auto max_compl = max({num_from_compl, num_to_compl, den_from_compl}); if constexpr (max_compl > 1) { if constexpr (num_from_compl == max_compl) { @@ -1225,8 +1216,8 @@ template(type_list{}, den_from, type_list{}, den_to); } else { - constexpr auto num_from_compl = get_complexity(NumFrom{}); - constexpr auto num_to_compl = get_complexity(NumTo{}); + constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value; + constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value; constexpr auto max_compl = max(num_from_compl, num_to_compl); if constexpr (max_compl > 1) { if constexpr (num_from_compl == max_compl) { @@ -1253,8 +1244,8 @@ template(num_from, type_list{}, num_to, type_list{}); else { - constexpr auto den_from_compl = get_complexity(DenFrom{}); - constexpr auto den_to_compl = get_complexity(DenTo{}); + constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value; + constexpr auto den_to_compl = decltype(get_complexity(DenTo{}))::value; constexpr auto max_compl = max(den_from_compl, den_to_compl); if constexpr (max_compl > 1) { if constexpr (den_from_compl == max_compl) { @@ -1371,14 +1362,14 @@ template return res == no ? no : yes; }; if constexpr ((NamedQuantitySpec && NamedQuantitySpec) || - get_complexity(from_kind) == get_complexity(to_kind)) + decltype(get_complexity(from_kind))::value == decltype(get_complexity(to_kind))::value) return convertible_impl(from_kind, to_kind); - else if constexpr (get_complexity(from_kind) > get_complexity(to_kind)) - return exploded_kind_result( - convertible_impl(get_kind_tree_root(explode(from_kind).quantity), to_kind)); + else if constexpr (decltype(get_complexity(from_kind))::value > decltype(get_complexity(to_kind))::value) + return exploded_kind_result(convertible_impl( + get_kind_tree_root(explode(from_kind).quantity), to_kind)); else - return exploded_kind_result( - convertible_impl(from_kind, get_kind_tree_root(explode(to_kind).quantity))); + return exploded_kind_result(convertible_impl( + from_kind, get_kind_tree_root(explode(to_kind).quantity))); } template @@ -1393,11 +1384,11 @@ template return no; } else if constexpr (get_kind(From{}) != get_kind(To{})) return no; - else if constexpr (get_complexity(From{}) != get_complexity(To{})) { - if constexpr (get_complexity(From{}) > get_complexity(To{})) - return convertible_impl(explode(from).quantity, to); + else if constexpr (decltype(get_complexity(From{}))::value != decltype(get_complexity(To{}))::value) { + if constexpr (decltype(get_complexity(From{}))::value > decltype(get_complexity(To{}))::value) + return convertible_impl(explode(from).quantity, to); else { - auto res = explode(to); + auto res = explode(to); return min(res.result, convertible_impl(from, res.quantity)); } } @@ -1422,7 +1413,7 @@ template } else if constexpr (DerivedQuantitySpec && DerivedQuantitySpec) { return are_ingredients_convertible(from, to); } else if constexpr (DerivedQuantitySpec) { - auto res = explode(from); + auto res = explode(from); if constexpr (NamedQuantitySpec) return convertible_impl(res.quantity, to); else if constexpr (requires { to._equation_; }) { @@ -1431,7 +1422,7 @@ template } else return are_ingredients_convertible(from, to); } else if constexpr (DerivedQuantitySpec) { - auto res = explode(to); + auto res = explode(to); if constexpr (NamedQuantitySpec) return min(res.result, convertible_impl(from, res.quantity)); else if constexpr (requires { from._equation_; }) @@ -1508,8 +1499,8 @@ template } else if constexpr (requires { Q::_parent_; }) { return get_kind_tree_root(Q::_parent_); } else if constexpr (detail::DerivedQuantitySpec) { - return detail::expr_map(q); + return decltype(detail::expr_map(q)){}; } else { // root quantity return q;