summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--vespalib/src/tests/coro/generator/CMakeLists.txt1
-rw-r--r--vespalib/src/tests/coro/generator/generator_bench.cpp50
-rw-r--r--vespalib/src/tests/coro/generator/hidden_sequence.cpp21
-rw-r--r--vespalib/src/tests/coro/generator/hidden_sequence.h8
-rw-r--r--vespalib/src/vespa/vespalib/coro/generator.h61
5 files changed, 88 insertions, 53 deletions
diff --git a/vespalib/src/tests/coro/generator/CMakeLists.txt b/vespalib/src/tests/coro/generator/CMakeLists.txt
index e2534274f7c..d2c8ec9b857 100644
--- a/vespalib/src/tests/coro/generator/CMakeLists.txt
+++ b/vespalib/src/tests/coro/generator/CMakeLists.txt
@@ -9,6 +9,7 @@ vespa_add_executable(vespalib_generator_test_app TEST
vespa_add_executable(vespalib_generator_bench_app TEST
SOURCES
generator_bench.cpp
+ hidden_sequence.cpp
DEPENDS
vespalib
GTest::GTest
diff --git a/vespalib/src/tests/coro/generator/generator_bench.cpp b/vespalib/src/tests/coro/generator/generator_bench.cpp
index ae0e0fce894..4fa9c6186f5 100644
--- a/vespalib/src/tests/coro/generator/generator_bench.cpp
+++ b/vespalib/src/tests/coro/generator/generator_bench.cpp
@@ -1,5 +1,6 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "hidden_sequence.h"
#include <vespa/vespalib/coro/generator.h>
#include <vespa/vespalib/util/benchmark_timer.h>
#include <vespa/vespalib/util/sequence.h>
@@ -11,23 +12,8 @@ using vespalib::coro::Generator;
using vespalib::BenchmarkTimer;
using vespalib::Sequence;
-size_t calc_sum(const std::vector<size_t> &values) {
- size_t sum = 0;
- for (auto&& value: values) {
- sum += value;
- }
- return sum;
-}
-
-size_t calc_sum(Generator<size_t> values) {
- size_t sum = 0;
- for (auto&& value: values) {
- sum += value;
- }
- return sum;
-}
-
-size_t calc_sum(Generator<const size_t &> values) {
+template <std::ranges::input_range T>
+size_t calc_sum(T &&values) {
size_t sum = 0;
for (auto&& value: values) {
sum += value;
@@ -42,6 +28,7 @@ size_t calc_sum(Sequence<size_t> &seq) {
}
return sum;
}
+size_t calc_sum(Sequence<size_t>::UP &ptr) { return calc_sum(*ptr); }
std::vector<size_t> make_data() __attribute__((noinline));
std::vector<size_t> make_data() {
@@ -83,9 +70,9 @@ size_t calc_sum_generator(const std::vector<size_t> &data) {
return calc_sum(gen_values(data));
}
-Generator<const size_t &> gen_values_noinline(const std::vector<size_t> &data) __attribute__((noinline));
-Generator<const size_t &> gen_values_noinline(const std::vector<size_t> &data) {
- for (const size_t &value: data) {
+Generator<size_t> gen_values_noinline(const std::vector<size_t> &data) __attribute__((noinline));
+Generator<size_t> gen_values_noinline(const std::vector<size_t> &data) {
+ for (size_t value: data) {
co_yield value;
}
}
@@ -104,22 +91,37 @@ double bench(auto fun, const std::vector<size_t> &data, size_t &res) {
return timer.min_time() * 1000.0;
}
+double bench_indirect(auto factory, const std::vector<size_t> &data, size_t &res) {
+ BenchmarkTimer timer(5.0);
+ while (timer.has_budget()) {
+ auto seq = factory(data);
+ timer.before();
+ res = calc_sum(seq);
+ timer.after();
+ }
+ return timer.min_time() * 1000.0;
+}
+
TEST(GeneratorBench, direct_vs_generated_for_loop) {
auto data = make_data();
- size_t result[4] = { 0, 1, 2, 3 };
+ size_t result[5] = { 0, 1, 2, 3, 4 };
double sequence_ms = bench(calc_sum_sequence, data, result[0]);
fprintf(stderr, "sequence: %g ms\n", sequence_ms);
- double generator_noinline_ms = bench(calc_sum_generator_noinline, data, result[1]);
+ double hidden_sequence_ms = bench_indirect(make_ext_seq, data, result[1]);
+ fprintf(stderr, "hidden sequence: %g ms\n", hidden_sequence_ms);
+ double generator_noinline_ms = bench(calc_sum_generator_noinline, data, result[2]);
fprintf(stderr, "generator_noinline: %g ms\n", generator_noinline_ms);
- double generator_ms = bench(calc_sum_generator, data, result[2]);
+ double generator_ms = bench(calc_sum_generator, data, result[3]);
fprintf(stderr, "generator: %g ms\n", generator_ms);
- double direct_ms = bench(calc_sum_direct, data, result[3]);
+ double direct_ms = bench(calc_sum_direct, data, result[4]);
fprintf(stderr, "direct: %g ms\n", direct_ms);
EXPECT_EQ(result[0], result[1]);
EXPECT_EQ(result[0], result[2]);
EXPECT_EQ(result[0], result[3]);
+ EXPECT_EQ(result[0], result[4]);
fprintf(stderr, "ratio (generator/direct): %g\n", (generator_ms/direct_ms));
fprintf(stderr, "ratio (generator_noinline/generator): %g\n", (generator_noinline_ms/generator_ms));
+ fprintf(stderr, "ratio (sequence/generator_noinline): %g\n", (sequence_ms/generator_noinline_ms));
fprintf(stderr, "ratio (sequence/generator): %g\n", (sequence_ms/generator_ms));
}
diff --git a/vespalib/src/tests/coro/generator/hidden_sequence.cpp b/vespalib/src/tests/coro/generator/hidden_sequence.cpp
new file mode 100644
index 00000000000..39eedb4623c
--- /dev/null
+++ b/vespalib/src/tests/coro/generator/hidden_sequence.cpp
@@ -0,0 +1,21 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "hidden_sequence.h"
+
+namespace {
+
+struct MyHiddenSeq : vespalib::Sequence<size_t> {
+ const std::vector<size_t> &data;
+ size_t pos;
+ MyHiddenSeq(const std::vector<size_t> &data_in)
+ : data(data_in), pos(0) {}
+ bool valid() const override { return pos < data.size(); }
+ size_t get() const override { return data[pos]; }
+ void next() override { ++pos; }
+};
+
+}
+
+vespalib::Sequence<size_t>::UP make_ext_seq(const std::vector<size_t> &data) {
+ return std::make_unique<MyHiddenSeq>(data);
+}
diff --git a/vespalib/src/tests/coro/generator/hidden_sequence.h b/vespalib/src/tests/coro/generator/hidden_sequence.h
new file mode 100644
index 00000000000..917b50b1129
--- /dev/null
+++ b/vespalib/src/tests/coro/generator/hidden_sequence.h
@@ -0,0 +1,8 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/vespalib/util/sequence.h>
+#include <vector>
+
+vespalib::Sequence<size_t>::UP make_ext_seq(const std::vector<size_t> &data);
diff --git a/vespalib/src/vespa/vespalib/coro/generator.h b/vespalib/src/vespa/vespalib/coro/generator.h
index 9f6b6c9fde5..1f1468d1d19 100644
--- a/vespalib/src/vespa/vespalib/coro/generator.h
+++ b/vespalib/src/vespa/vespalib/coro/generator.h
@@ -24,51 +24,54 @@ namespace vespalib::coro {
* make it easier for compilers to perform HALO, code inlining and
* even constant folding.
**/
-template <typename T, typename ValueType = std::remove_cvref<T>>
+template <typename R, typename V = void>
class [[nodiscard]] Generator {
public:
- using value_type = ValueType;
- using Pointer = std::add_pointer_t<T>;
+ // these are from the std::generator proposal (P2502R2)
+ using value_type = std::conditional_t<std::is_void_v<V>, std::remove_cvref_t<R>, V>;
+ using ref_type = std::conditional_t<std::is_void_v<V>, R &&, R>;
+ using yield_type = std::conditional_t<std::is_reference_v<ref_type>, ref_type, const ref_type &>;
+ using cref_yield = const std::remove_reference_t<yield_type> &;
+ using ptr_type = std::add_pointer_t<yield_type>;
+ using cpy_type = std::remove_cvref_t<yield_type>;
+ static constexpr bool extra_yield = std::is_rvalue_reference_v<yield_type> && std::constructible_from<cpy_type, cref_yield>;
class promise_type;
using Handle = std::coroutine_handle<promise_type>;
class promise_type {
private:
- Pointer _ptr;
+ ptr_type _state;
+ struct copy_awaiter : std::suspend_always {
+ copy_awaiter(const cpy_type &value, ptr_type &ptr)
+ : value_cpy(value)
+ {
+ ptr = std::addressof(value_cpy);
+ }
+ copy_awaiter(copy_awaiter&&) = delete;
+ copy_awaiter(const copy_awaiter&) = delete;
+ cpy_type value_cpy;
+ };
+
public:
promise_type(promise_type &&) = delete;
promise_type(const promise_type &) = delete;
- promise_type() noexcept : _ptr(nullptr) {}
- Generator<T> get_return_object() { return Generator(Handle::from_promise(*this)); }
+ promise_type() noexcept : _state() {}
+ Generator get_return_object() { return Generator(Handle::from_promise(*this)); }
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
- std::suspend_always yield_value(T &&value) noexcept {
- _ptr = &value;
+ std::suspend_always yield_value(yield_type value) noexcept {
+ _state = std::addressof(value);
return {};
}
- auto yield_value(const T &value)
- noexcept(std::is_nothrow_constructible_v<T, const T &>)
- requires(!std::is_reference_v<T> && std::copy_constructible<T>)
- {
- struct awaiter : std::suspend_always {
- awaiter(const T &value_in, Pointer &ptr)
- noexcept(std::is_nothrow_constructible_v<T, const T &>)
- : value_cpy(value_in)
- {
- ptr = std::addressof(value_cpy);
- }
- awaiter(awaiter&&) = delete;
- awaiter(const awaiter&) = delete;
- T value_cpy;
- };
- return awaiter(value, _ptr);
+ auto yield_value(cref_yield value) requires extra_yield {
+ return copy_awaiter(value, _state);
}
void return_void() noexcept {}
void unhandled_exception() { throw; }
- T &&result() noexcept {
- return std::forward<T>(*_ptr);
+ ref_type result() noexcept {
+ return static_cast<ref_type>(*_state);
}
template<typename U> U &&await_transform(U &&value) = delete;
};
@@ -87,7 +90,7 @@ public:
}
using iterator_concept = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
- using value_type = std::remove_cvref_t<T>;
+ using value_type = Generator::value_type;
bool operator==(std::default_sentinel_t) const {
return _handle.done();
}
@@ -98,8 +101,8 @@ public:
void operator++(int) {
operator++();
}
- decltype(auto) operator*() const {
- return std::forward<T>(_handle.promise().result());
+ ref_type operator*() const {
+ return _handle.promise().result();
}
};