aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorHåvard Pettersen <havardpe@yahooinc.com>2022-09-19 13:26:57 +0000
committerHåvard Pettersen <havardpe@yahooinc.com>2022-09-19 13:37:20 +0000
commit364dedfcc14b97726f1df430610266a13bd60863 (patch)
tree35b2418e8678882d6e78d647c52f9d759be2ee64 /vespalib
parent7a9cfb70e27e921e20d23a841e9b4257ddeaa5dd (diff)
more flexible ThreadBundle::run variants
Diffstat (limited to 'vespalib')
-rw-r--r--vespalib/src/tests/simple_thread_bundle/simple_thread_bundle_test.cpp42
-rw-r--r--vespalib/src/vespa/vespalib/util/small_vector.h26
-rw-r--r--vespalib/src/vespa/vespalib/util/thread_bundle.h36
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 &target; }
template <typename T>
Runnable *resolve(const std::unique_ptr<T> &target) { return target.get(); }