diff options
author | Håvard Pettersen <havardpe@yahooinc.com> | 2022-09-19 13:26:57 +0000 |
---|---|---|
committer | Håvard Pettersen <havardpe@yahooinc.com> | 2022-09-19 13:37:20 +0000 |
commit | 364dedfcc14b97726f1df430610266a13bd60863 (patch) | |
tree | 35b2418e8678882d6e78d647c52f9d759be2ee64 /vespalib | |
parent | 7a9cfb70e27e921e20d23a841e9b4257ddeaa5dd (diff) |
more flexible ThreadBundle::run variants
Diffstat (limited to 'vespalib')
3 files changed, 63 insertions, 41 deletions
diff --git a/vespalib/src/tests/simple_thread_bundle/simple_thread_bundle_test.cpp b/vespalib/src/tests/simple_thread_bundle/simple_thread_bundle_test.cpp index 27e75315dfc..59aeddfe8ca 100644 --- a/vespalib/src/tests/simple_thread_bundle/simple_thread_bundle_test.cpp +++ b/vespalib/src/tests/simple_thread_bundle/simple_thread_bundle_test.cpp @@ -3,7 +3,9 @@ #include <vespa/vespalib/util/simple_thread_bundle.h> #include <vespa/vespalib/util/exceptions.h> #include <vespa/vespalib/util/box.h> +#include <vespa/vespalib/util/small_vector.h> #include <thread> +#include <forward_list> using namespace vespalib; using namespace vespalib::fixed_thread_bundle; @@ -225,10 +227,15 @@ TEST("require that Proxy needs fixup to become Runnable") { } TEST_FF("require that various versions of run can be used to invoke targets", SimpleThreadBundle(5), State(5)) { - EXPECT_TRUE(ThreadBundle::is_runnable_ptr<Runnable*>()); - EXPECT_TRUE(ThreadBundle::is_runnable_ptr<Runnable::UP>()); - EXPECT_FALSE(ThreadBundle::is_runnable_ptr<std::unique_ptr<Proxy>>()); - EXPECT_FALSE(ThreadBundle::is_runnable_ptr<std::unique_ptr<AlmostRunnable>>()); + EXPECT_TRUE(thread_bundle::direct_dispatch_array<std::vector<Runnable*>>); + EXPECT_TRUE(thread_bundle::direct_dispatch_array<SmallVector<Runnable*>>); + EXPECT_TRUE(thread_bundle::direct_dispatch_array<std::initializer_list<Runnable*>>); + EXPECT_TRUE(thread_bundle::direct_dispatch_array<std::vector<Runnable::UP>>); + EXPECT_TRUE(thread_bundle::direct_dispatch_array<SmallVector<Runnable::UP>>); + EXPECT_TRUE(thread_bundle::direct_dispatch_array<std::initializer_list<Runnable::UP>>); + EXPECT_FALSE(thread_bundle::direct_dispatch_array<std::forward_list<Runnable*>>); + EXPECT_FALSE(thread_bundle::direct_dispatch_array<std::vector<std::unique_ptr<Proxy>>>); + EXPECT_FALSE(thread_bundle::direct_dispatch_array<std::vector<std::unique_ptr<AlmostRunnable>>>); std::vector<Runnable::UP> direct; std::vector<std::unique_ptr<Proxy>> custom; for (Runnable &target: f2.cnts) { @@ -237,21 +244,32 @@ TEST_FF("require that various versions of run can be used to invoke targets", Si } std::vector<Runnable*> refs = f2.getTargets(5); f2.check({0,0,0,0,0}); - f1.run(refs.data(), 3); + f1.run(refs.data(), 3); // baseline f2.check({1,1,1,0,0}); - f1.run(&refs[3], 2); + f1.run(&refs[3], 2); // baseline f2.check({1,1,1,1,1}); - f1.run(refs); + f1.run(f2.getTargets(5)); // const fast dispatch f2.check({2,2,2,2,2}); - f1.run(direct); + f1.run(refs); // non-const fast dispatch f2.check({3,3,3,3,3}); - f1.run(custom); + f1.run(direct); // fast dispatch with transparent UP f2.check({4,4,4,4,4}); - f1.run(f2.cnts); + f1.run(custom); // fall-back with runnable subclass UP f2.check({5,5,5,5,5}); - std::initializer_list<std::reference_wrapper<Cnt>> list = {f2.cnts[0], f2.cnts[1], f2.cnts[2], f2.cnts[3], f2.cnts[4]}; - f1.run(list); + f1.run(f2.cnts); // fall-back with resolved reference (actual objects) f2.check({6,6,6,6,6}); + std::initializer_list<std::reference_wrapper<Cnt>> list = {f2.cnts[0], f2.cnts[1], f2.cnts[2], f2.cnts[3], f2.cnts[4]}; + f1.run(list); // fall-back with resolved reference (reference wrapper) + f2.check({7,7,7,7,7}); + std::initializer_list<Runnable*> list2 = {&f2.cnts[0], &f2.cnts[1], &f2.cnts[2], &f2.cnts[3], &f2.cnts[4]}; + f1.run(list2); // fast dispatch with non-vector range + f2.check({8,8,8,8,8}); + std::forward_list<Runnable*> run_list(list2); + f1.run(run_list); // fall-back with non-sized range + f2.check({9,9,9,9,9}); + vespalib::SmallVector<Runnable*> my_vec(list2); + f1.run(my_vec); // fast dispatch with custom container + f2.check({10,10,10,10,10}); } TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/vespa/vespalib/util/small_vector.h b/vespalib/src/vespa/vespalib/util/small_vector.h index 7df3913f376..aaf8d848b54 100644 --- a/vespalib/src/vespa/vespalib/util/small_vector.h +++ b/vespalib/src/vespa/vespalib/util/small_vector.h @@ -176,21 +176,21 @@ public: } } operator ConstArrayRef<T> () const { return ConstArrayRef<T>(data(), size()); } - bool empty() const { return (_size == 0); } - uint32_t size() const { return _size; } - uint32_t capacity() const { return _capacity; } - bool is_local() const { return (_data == local()); } - T *begin() { return _data; } - T *end() { return (_data + _size); } - const T *begin() const { return _data; } - const T *end() const { return (_data + _size); } - T &operator[](size_t idx) { return _data[idx]; } - const T &operator[](size_t idx) const { return _data[idx]; } + bool empty() const noexcept { return (_size == 0); } + uint32_t size() const noexcept { return _size; } + uint32_t capacity() const noexcept { return _capacity; } + bool is_local() const noexcept { return (_data == local()); } + T *begin() noexcept { return _data; } + T *end() noexcept { return (_data + _size); } + const T *begin() const noexcept { return _data; } + const T *end() const noexcept { return (_data + _size); } + T &operator[](size_t idx) noexcept { return _data[idx]; } + const T &operator[](size_t idx) const noexcept { return _data[idx]; } T *data() noexcept { return _data; } const T *data() const noexcept { return _data; } - T &back() { return _data[_size - 1]; } - const T &back() const { return _data[_size - 1]; } - void clear() { + T &back() noexcept { return _data[_size - 1]; } + const T &back() const noexcept { return _data[_size - 1]; } + void clear() noexcept { small_vector::destroy_objects(_data, _size); _size = 0; } diff --git a/vespalib/src/vespa/vespalib/util/thread_bundle.h b/vespalib/src/vespa/vespalib/util/thread_bundle.h index cfdedb347c8..252e8976544 100644 --- a/vespalib/src/vespa/vespalib/util/thread_bundle.h +++ b/vespalib/src/vespa/vespalib/util/thread_bundle.h @@ -8,6 +8,16 @@ namespace vespalib { +namespace thread_bundle { + +template <typename T> +concept direct_dispatch_array = std::ranges::contiguous_range<T> && + std::ranges::sized_range<T> && + (std::is_same_v<std::ranges::range_value_t<T>,Runnable*> || + std::is_same_v<std::ranges::range_value_t<T>,Runnable::UP>); + +} + /** * Interface used to separate the ownership and deployment of a * collection of threads cooperating to perform a partitioned @@ -31,31 +41,24 @@ struct ThreadBundle { virtual void run(Runnable* const* targets, size_t cnt) = 0; // convenience run wrapper - void run(const std::vector<Runnable*> &targets) { - run(targets.data(), targets.size()); - } - - // convenience run wrapper - void run(const std::vector<Runnable::UP> &targets) { - static_assert(sizeof(Runnable::UP) == sizeof(Runnable*)); - run(reinterpret_cast<Runnable* const*>(targets.data()), targets.size()); - } - - template <typename T> - static constexpr bool is_runnable_ptr() { - return (std::is_same_v<T,Runnable*> || std::is_same_v<T,Runnable::UP>); + template <thread_bundle::direct_dispatch_array Array> + void run(const Array &items) { + static_assert(sizeof(std::ranges::range_value_t<Array>) == sizeof(Runnable*)); + run(reinterpret_cast<Runnable* const*>(std::ranges::data(items)), std::ranges::size(items)); } // convenience run wrapper template <std::ranges::range List> - requires (!is_runnable_ptr<std::ranges::range_value_t<List>>()) + requires (!thread_bundle::direct_dispatch_array<List>) void run(List &items) { std::vector<Runnable*> targets; - targets.reserve(std::ranges::size(items)); + if constexpr (std::ranges::sized_range<List>) { + targets.reserve(std::ranges::size(items)); + } for (auto &item: items) { targets.push_back(resolve(item)); } - run(targets); + run(targets.data(), targets.size()); } /** @@ -67,6 +70,7 @@ struct ThreadBundle { static ThreadBundle &trivial(); private: + Runnable *resolve(Runnable *target) { return target; } Runnable *resolve(Runnable &target) { return ⌖ } template <typename T> Runnable *resolve(const std::unique_ptr<T> &target) { return target.get(); } |