From 3d85c437b2d513febcf106bdf27a7ab7678c1121 Mon Sep 17 00:00:00 2001 From: Anastasia Stulova Date: Mon, 15 May 2023 03:31:16 -0700 Subject: [PATCH 1/2] Update cartesian product to provide better perf for parallel execution --- include/cartesian_product.hpp | 69 +++++++++++------------------------ 1 file changed, 22 insertions(+), 47 deletions(-) diff --git a/include/cartesian_product.hpp b/include/cartesian_product.hpp index 81fdcbd..59aaf84 100644 --- a/include/cartesian_product.hpp +++ b/include/cartesian_product.hpp @@ -1197,12 +1197,15 @@ namespace tl { class cursor { template using constify = std::conditional_t; - + template + using intify = std::conditional_t; // Instead of storing a pointer to the views, we'll store the sentinels: // constify>* bases_; std::tuple>...> currents_{}; std::tuple>...> begins_{}; std::tuple>...> ends_{}; + std::tuple...> counts_{}; + int idx_; public: using reference = @@ -1217,6 +1220,8 @@ namespace tl { : currents_( tl::tuple_transform(std::ranges::begin, *bases) ) , begins_( currents_ ) , ends_( tl::tuple_transform(std::ranges::end, *bases) ) + , counts_( tl::tuple_transform(std::ranges::size, *bases) ) + , idx_(0) {} //If the underlying ranges are common, we can get to the end by assigning from end @@ -1246,66 +1251,36 @@ namespace tl { return tuple_transform([](auto& i) -> decltype(auto) { return *i; }, currents_); } + template + void update(int idx) { + if constexpr(N == 0) + std::get(currents_) = idx + std::get(begins_); + else + std::get(currents_) = idx % std::get(counts_) + std::get(begins_); + if constexpr (N > 0) { + idx /= std::get(counts_); + update(idx); + } + } + //Increment the iterator at std::get(currents_) //If that iterator hits its end, recurse to std::get template void next() { - auto& it = std::get(currents_); - ++it; - if (it == std::get(ends_)) { - if constexpr (N > 0) { - it = std::get(begins_); - next(); - } - } + advance(1); } //Decrement the iterator at std::get(currents_) //If that iterator was at its begin, cycle it to end and recurse to std::get template void prev() requires (am_bidirectional...>) { - auto& it = std::get(currents_); - if (it == std::get(begins_)) { - std::ranges::advance(it, std::get(ends_)); - if constexpr (N > 0) { - prev(); - } - } - --it; + advance(-1); } template void advance(difference_type n) requires (am_random_access...>) { - auto& it = std::get(currents_); - auto begin = std::get(begins_); - auto end = std::get(ends_); - auto size = end - begin; - - auto distance_from_begin = it - begin; - - //Calculate where in the iterator cycle we should end up - auto offset = (distance_from_begin + n) % static_cast(size); - - //Calculate how many times incrementing this iterator would cause it to cycle round - //This will be negative if we cycled by decrementing - auto times_cycled = (distance_from_begin + n) / size - (offset < 0 ? 1 : 0); - - //Set the iterator to the correct new position - it = begin + (offset < 0 ? offset + static_cast(size) : offset); - - if constexpr (N > 0) { - //If this iterator cycled, then we need to advance the N-1th iterator - //by the number of times it cycled - if (times_cycled != 0) { - advance(times_cycled); - } - } - else { - //If we're the 0th iterator, then cycling should set the iterator to the end - if (times_cycled > 0) { - std::ranges::advance(it, end); - } - } + idx_ += n; + update(idx_); } constexpr bool equal(const cursor& rhs) const From 436943a172905f07dd3e635a64238319f9ff2fef Mon Sep 17 00:00:00 2001 From: Anastasia Stulova Date: Mon, 15 May 2023 03:38:55 -0700 Subject: [PATCH 2/2] Avoid using touples when advancing the iterator position --- include/cartesian_product.hpp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/include/cartesian_product.hpp b/include/cartesian_product.hpp index 59aaf84..159c01b 100644 --- a/include/cartesian_product.hpp +++ b/include/cartesian_product.hpp @@ -1206,6 +1206,9 @@ namespace tl { std::tuple>...> ends_{}; std::tuple...> counts_{}; int idx_; + // FIXME: This won't work when iterators are different, is there any common base type? + std::tuple_element_t<0, decltype(currents_)> x[sizeof...(Vs)], xb[sizeof...(Vs)]; + int xc[sizeof...(Vs)]; public: using reference = @@ -1216,13 +1219,23 @@ namespace tl { using difference_type = std::ptrdiff_t; cursor() = default; + constexpr explicit cursor(constify>* bases) : currents_( tl::tuple_transform(std::ranges::begin, *bases) ) , begins_( currents_ ) , ends_( tl::tuple_transform(std::ranges::end, *bases) ) , counts_( tl::tuple_transform(std::ranges::size, *bases) ) , idx_(0) - {} + { init(); } + + template + void init() { + x[N] = std::get(currents_); + xb[N] = std::get(begins_); + xc[N] = std::get(counts_); + if constexpr(N > 0) + init(); + } //If the underlying ranges are common, we can get to the end by assigning from end constexpr explicit cursor(as_sentinel_t, constify>* bases) @@ -1252,7 +1265,7 @@ namespace tl { } template - void update(int idx) { + void update_(int idx) { if constexpr(N == 0) std::get(currents_) = idx + std::get(begins_); else @@ -1262,6 +1275,19 @@ namespace tl { update(idx); } } + // Update iterators without using touples. + template + void update(int idx) { + if constexpr(N == 0) + x[N] = idx + xb[N]; + else + x[N] = idx % xc[N] + xb[N]; + std::get(currents_) = x[N]; + if constexpr (N > 0) { + idx /= xc[N]; + update(idx); + } + } //Increment the iterator at std::get(currents_) //If that iterator hits its end, recurse to std::get