Skip to content

Commit 2d3b2c4

Browse files
Merge pull request #1368 from PowerGridModel/pgm/feature/performance-prepare-scenario
Performance: only check independence for filled component scenarios
2 parents b14330c + 2107db7 commit 2d3b2c4

2 files changed

Lines changed: 85 additions & 55 deletions

File tree

power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,22 @@
1010
#include "meta_data.hpp"
1111

1212
#include "../common/common.hpp"
13+
#include "../common/counting_iterator.hpp"
1314
#include "../common/exception.hpp"
1415
#include "../common/iterator_facade.hpp"
16+
#include "../common/typing.hpp"
1517

1618
#include <algorithm>
1719
#include <cassert>
1820
#include <concepts>
1921
#include <cstddef>
22+
#include <iterator>
2023
#include <ranges>
2124
#include <span>
2225
#include <string>
2326
#include <string_view>
2427
#include <type_traits>
28+
#include <utility>
2529
#include <vector>
2630

2731
namespace power_grid_model {
@@ -216,6 +220,8 @@ template <dataset_type_tag dataset_type_> class Dataset {
216220
std::span<Indptr> indptr{};
217221
};
218222

223+
template <class StructType> using SpanRange = std::span<StructType>;
224+
219225
template <class StructType>
220226
using RangeObject = std::conditional_t<is_data_mutable_v<dataset_type>, mutable_range_object<StructType>,
221227
const_range_object<StructType>>;
@@ -452,7 +458,7 @@ template <dataset_type_tag dataset_type_> class Dataset {
452458
// get buffer by component type
453459
template <class type_getter, class ComponentType,
454460
class StructType = DataStruct<typename type_getter::template type<ComponentType>>>
455-
std::span<StructType> get_buffer_span(Idx scenario = invalid_index) const {
461+
SpanRange<StructType> get_buffer_span(Idx scenario = invalid_index) const {
456462
assert(scenario < batch_size());
457463

458464
if (!is_batch() && scenario > 0) {
@@ -476,27 +482,17 @@ template <dataset_type_tag dataset_type_> class Dataset {
476482
return get_columnar_buffer_span_impl<StructType>(scenario, idx);
477483
}
478484

479-
// get buffer by component type for all scenarios in vector span
485+
// get buffer by component type for all non-empty scenarios in vector span
480486
template <class type_getter, class ComponentType,
481487
class StructType = DataStruct<typename type_getter::template type<ComponentType>>>
482-
std::vector<std::span<StructType>> get_buffer_span_all_scenarios() const {
483-
Idx const idx = find_component(ComponentType::name, false);
484-
std::vector<std::span<StructType>> result(batch_size());
485-
for (Idx scenario{}; scenario != batch_size(); scenario++) {
486-
result[scenario] = get_buffer_span_impl<StructType>(scenario, idx);
487-
}
488-
return result;
488+
std::vector<SpanRange<StructType>> get_buffer_span_all_scenarios() const {
489+
return get_buffer_span_all_scenarios_impl<type_getter, ComponentType, SpanRange>();
489490
}
490491

491492
template <class type_getter, class ComponentType,
492493
class StructType = DataStruct<typename type_getter::template type<ComponentType>>>
493494
std::vector<RangeObject<StructType>> get_columnar_buffer_span_all_scenarios() const {
494-
Idx const idx = find_component(ComponentType::name, false);
495-
std::vector<RangeObject<StructType>> result(batch_size());
496-
for (Idx scenario{}; scenario != batch_size(); scenario++) {
497-
result[scenario] = get_columnar_buffer_span_impl<StructType>(scenario, idx);
498-
}
499-
return result;
495+
return get_buffer_span_all_scenarios_impl<type_getter, ComponentType, RangeObject>();
500496
}
501497

502498
// get individual dataset from batch
@@ -559,7 +555,7 @@ template <dataset_type_tag dataset_type_> class Dataset {
559555

560556
template <class type_getter, class ComponentType, typename Func,
561557
class StructType = DataStruct<typename type_getter::template type<ComponentType>>>
562-
requires std::invocable<Func, std::span<StructType>> && std::invocable<Func, RangeObject<StructType>>
558+
requires std::invocable<Func, SpanRange<StructType>> && std::invocable<Func, RangeObject<StructType>>
563559
auto for_each_component(Func&& func, Idx scenario = invalid_index) const {
564560
if (is_columnar(ComponentType::name)) {
565561
return std::forward<Func>(func)(get_columnar_buffer_span<type_getter, ComponentType, StructType>(scenario));
@@ -659,31 +655,53 @@ template <dataset_type_tag dataset_type_> class Dataset {
659655
std::begin(total_range) + info.elements_per_scenario * (scenario + 1)};
660656
}
661657

662-
// get non-empty row buffer
663-
template <class StructType> std::span<StructType> get_buffer_span_impl(Idx scenario, Idx component_idx) const {
658+
template <typename StructType, template <typename> typename RangeType>
659+
requires std::ranges::view<RangeType<StructType>> &&
660+
(std::same_as<RangeType<StructType>, SpanRange<StructType>> ||
661+
std::same_as<RangeType<StructType>, RangeObject<StructType>>)
662+
auto get_buffer_view_impl(Idx scenario, Idx component_idx) const {
663+
using RangeType_ = RangeType<StructType>;
664+
664665
// return empty span if the component does not exist
665666
if (component_idx < 0) {
666-
return {};
667+
return RangeType_{};
667668
}
668669
// return span based on uniform or non-uniform buffer
669670
ComponentInfo const& info = dataset_info_.component_info[component_idx];
670671
Buffer const& buffer = buffers_[component_idx];
671-
auto const ptr = reinterpret_cast<StructType*>(buffer.data);
672-
return get_span_impl(std::span<StructType>{ptr, ptr + info.total_elements}, scenario, buffer, info);
672+
673+
if constexpr (std::same_as<RangeType_, SpanRange<StructType>>) {
674+
assert(is_row_based(buffer) || info.total_elements == 0);
675+
auto const ptr = reinterpret_cast<StructType*>(buffer.data);
676+
return get_span_impl(RangeType_{ptr, ptr + info.total_elements}, scenario, buffer, info);
677+
} else if constexpr (std::same_as<RangeType_, RangeObject<StructType>>) {
678+
assert(is_columnar(buffer));
679+
return get_span_impl(RangeType_{info.total_elements, buffer.attributes}, scenario, buffer, info);
680+
} else {
681+
static_assert(false, "only span and RangeObject are supported");
682+
}
683+
}
684+
685+
// get non-empty row buffer
686+
template <class StructType> SpanRange<StructType> get_buffer_span_impl(Idx scenario, Idx component_idx) const {
687+
return get_buffer_view_impl<StructType, SpanRange>(scenario, component_idx);
673688
}
674689

675690
// get non-empty columnar buffer
676691
template <class StructType>
677692
RangeObject<StructType> get_columnar_buffer_span_impl(Idx scenario, Idx component_idx) const {
678-
// return empty span if the component does not exist
679-
if (component_idx < 0) {
680-
return {};
681-
}
682-
// return span based on uniform or non-uniform buffer
683-
ComponentInfo const& info = dataset_info_.component_info[component_idx];
684-
Buffer const& buffer = buffers_[component_idx];
685-
assert(is_columnar(buffer));
686-
return get_span_impl(RangeObject<StructType>{info.total_elements, buffer.attributes}, scenario, buffer, info);
693+
return get_buffer_view_impl<StructType, RangeObject>(scenario, component_idx);
694+
}
695+
696+
template <class type_getter, class ComponentType, template <typename> typename RangeType,
697+
typename StructType = DataStruct<typename type_getter::template type<ComponentType>>>
698+
requires std::ranges::view<RangeType<StructType>>
699+
std::vector<RangeType<StructType>> get_buffer_span_all_scenarios_impl() const {
700+
Idx const component_idx = find_component(ComponentType::name, false);
701+
return IdxRange{batch_size()} | std::views::transform([this, component_idx](Idx scenario) {
702+
return get_buffer_view_impl<StructType, RangeType>(scenario, component_idx);
703+
}) |
704+
std::ranges::to<std::vector>();
687705
}
688706
};
689707

power_grid_model_c/power_grid_model/include/power_grid_model/main_core/update.hpp

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ inline void iterate_component_sequence(Func func, Elements elements, std::span<I
4242
}
4343
}
4444

45-
template <typename T> bool check_id_na(T const& obj) {
45+
template <typename T> inline bool check_id_na(T const& obj) {
4646
if constexpr (requires { obj.id; }) {
4747
return is_nan(obj.id);
4848
} else if constexpr (requires { obj.get().id; }) {
@@ -83,38 +83,38 @@ struct UpdateCompProperties {
8383
}
8484
};
8585

86-
template <typename CompType> void process_buffer_span(auto const& all_spans, UpdateCompProperties& properties) {
87-
properties.ids_all_na = std::ranges::all_of(all_spans, [](auto const& vec) {
86+
inline bool get_all_ids_na(auto const& all_spans) {
87+
return std::ranges::all_of(all_spans, [](auto const& vec) {
8888
return std::ranges::all_of(vec, [](auto const& item) { return detail::check_id_na(item); });
8989
});
90-
properties.ids_part_na = std::ranges::any_of(all_spans,
91-
[](auto const& vec) {
92-
return std::ranges::any_of(vec, [](auto const& item) {
93-
return detail::check_id_na(item);
94-
});
95-
}) &&
96-
!properties.ids_all_na;
90+
}
91+
92+
inline bool get_any_ids_na(auto const& all_spans) {
93+
return std::ranges::any_of(all_spans, [](auto const& vec) {
94+
return std::ranges::any_of(vec, [](auto const& item) { return detail::check_id_na(item); });
95+
});
96+
}
9797

98+
template <typename CompType> inline bool get_update_ids_match(auto const& all_spans) {
9899
if (all_spans.empty()) {
99-
properties.update_ids_match = true;
100-
return;
100+
return true; // if there are no elements, we can consider the ids to match
101101
}
102+
102103
// Remember the begin iterator of the first scenario, then loop over the remaining scenarios and
103104
// check the ids
104105
auto const first_span = all_spans.front();
105106
// check the subsequent scenarios
106107
// only return true if ids of all scenarios match the ids of the first batch
107108

108-
properties.update_ids_match =
109-
std::ranges::all_of(all_spans.cbegin() + 1, all_spans.cend(), [&first_span](auto const& current_span) {
110-
return std::ranges::equal(current_span, first_span,
111-
[](typename CompType::UpdateType const& obj,
112-
typename CompType::UpdateType const& first) { return obj.id == first.id; });
113-
});
109+
return std::ranges::all_of(all_spans.cbegin() + 1, all_spans.cend(), [&first_span](auto const& current_span) {
110+
return std::ranges::equal(current_span, first_span,
111+
[](typename CompType::UpdateType const& obj,
112+
typename CompType::UpdateType const& first) { return obj.id == first.id; });
113+
});
114114
}
115115

116116
template <class CompType>
117-
UpdateCompProperties check_component_independence(ConstDataset const& update_data, Idx n_component) {
117+
inline UpdateCompProperties check_component_independence(ConstDataset const& update_data, Idx n_component) {
118118
UpdateCompProperties properties;
119119
auto const component_idx = update_data.find_component(CompType::name, false);
120120
properties.is_columnar = update_data.is_columnar(CompType::name);
@@ -126,13 +126,25 @@ UpdateCompProperties check_component_independence(ConstDataset const& update_dat
126126
properties.uniform ? update_data.uniform_elements_per_scenario(CompType::name) : utils::invalid_index;
127127
properties.elements_in_base = n_component;
128128

129-
if (properties.is_columnar) {
130-
process_buffer_span<CompType>(
131-
update_data.template get_columnar_buffer_span_all_scenarios<meta_data::update_getter_s, CompType>(),
132-
properties);
129+
if (!properties.has_any_elements) {
130+
properties.ids_all_na = true; // if there are no elements, we can consider all ids to be NA
131+
properties.ids_part_na =
132+
false; // if there are no elements, we cannot have some ids that are NA and some that are not
133+
properties.update_ids_match = true; // if there are no elements, we can consider the
133134
} else {
134-
process_buffer_span<CompType>(
135-
update_data.template get_buffer_span_all_scenarios<meta_data::update_getter_s, CompType>(), properties);
135+
auto const process_buffer_span = [&properties](auto const& all_spans) {
136+
assert(!all_spans.empty());
137+
properties.ids_all_na = get_all_ids_na(all_spans);
138+
properties.ids_part_na = (!properties.ids_all_na) && get_any_ids_na(all_spans);
139+
properties.update_ids_match = properties.uniform && get_update_ids_match<CompType>(all_spans);
140+
};
141+
if (properties.is_columnar) {
142+
process_buffer_span(
143+
update_data.template get_columnar_buffer_span_all_scenarios<meta_data::update_getter_s, CompType>());
144+
} else {
145+
process_buffer_span(
146+
update_data.template get_buffer_span_all_scenarios<meta_data::update_getter_s, CompType>());
147+
}
136148
}
137149

138150
return properties;

0 commit comments

Comments
 (0)