diff options
Diffstat (limited to 'vespalib')
19 files changed, 201 insertions, 131 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt index dc99146fd3f..6d19988b96b 100644 --- a/vespalib/CMakeLists.txt +++ b/vespalib/CMakeLists.txt @@ -46,6 +46,7 @@ vespa_define_module( src/tests/component src/tests/compress src/tests/compression + src/tests/coro/active_work src/tests/coro/async_io src/tests/coro/detached src/tests/coro/generator diff --git a/vespalib/src/tests/coro/active_work/CMakeLists.txt b/vespalib/src/tests/coro/active_work/CMakeLists.txt new file mode 100644 index 00000000000..b230e10dbc7 --- /dev/null +++ b/vespalib/src/tests/coro/active_work/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_active_work_test_app TEST + SOURCES + active_work_test.cpp + DEPENDS + vespalib + GTest::GTest +) +vespa_add_test(NAME vespalib_active_work_test_app COMMAND vespalib_active_work_test_app) diff --git a/vespalib/src/tests/coro/active_work/active_work_test.cpp b/vespalib/src/tests/coro/active_work/active_work_test.cpp new file mode 100644 index 00000000000..26c0c3dd71a --- /dev/null +++ b/vespalib/src/tests/coro/active_work/active_work_test.cpp @@ -0,0 +1,66 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/coro/lazy.h> +#include <vespa/vespalib/coro/schedule.h> +#include <vespa/vespalib/coro/completion.h> +#include <vespa/vespalib/coro/active_work.h> +#include <vespa/vespalib/util/time.h> +#include <vespa/vespalib/util/threadstackexecutor.h> +#include <vespa/vespalib/gtest/gtest.h> + +using namespace vespalib; +using namespace vespalib::coro; + +Lazy<int> make_expensive_task(Executor &executor, int value) { + co_await schedule(executor); + auto cpu_cost = 20ms; + std::this_thread::sleep_for(cpu_cost); + co_return value; +} + +Lazy<int> make_cheap_task(Executor &, int value) { + co_return value; +} + +Lazy<int> concurrent_sum(Executor &executor, std::vector<int> values, + std::function<Lazy<int>(Executor &,int)> make_task) +{ + std::vector<Lazy<int>> work; + for (int v: values) { + work.push_back(make_task(executor, v)); + } + ActiveWork active; + for (auto &task: work) { + active.start(task); + } + co_await active.join(); + int res = 0; + for (auto &task: work) { + res += co_await task; // await_ready == true + } + co_return res; +} + +TEST(ActiveWorkTest, run_expensive_subtasks_concurrently) { + vespalib::ThreadStackExecutor executor(8); + auto t0 = steady_clock::now(); + auto result = sync_wait(concurrent_sum(executor, {1, 2, 3, 4, 5, 6, 7, 8, + 9,10,11,12,13,14,15,16}, + make_expensive_task)); + auto td = steady_clock::now() - t0; + EXPECT_EQ(result, 136); + fprintf(stderr, "time spent: %zu ms\n", count_ms(td)); +} + +TEST(ActiveWorkTest, run_cheap_subtasks_concurrently) { + vespalib::ThreadStackExecutor executor(1); + auto t0 = steady_clock::now(); + auto result = sync_wait(concurrent_sum(executor, {1, 2, 3, 4, 5, 6, 7, 8, + 9,10,11,12,13,14,15,16}, + make_cheap_task)); + auto td = steady_clock::now() - t0; + EXPECT_EQ(result, 136); + fprintf(stderr, "time spent: %zu ms\n", count_ms(td)); +} + +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/vespalib/src/tests/objects/nbostream/nbostream_test.cpp b/vespalib/src/tests/objects/nbostream/nbostream_test.cpp index 5d7f5c0504b..20ea2bf5aaa 100644 --- a/vespalib/src/tests/objects/nbostream/nbostream_test.cpp +++ b/vespalib/src/tests/objects/nbostream/nbostream_test.cpp @@ -216,16 +216,6 @@ TEST_F("Test serializing vespalib::string", Fixture) f.assertSerialize(exp, val); } -TEST_F("Test serializing vespalib::Array", Fixture) -{ - vespalib::Array<int16_t> val; - val.resize(2); - val[0] = 0x0123; - val[1] = 0x4567; - ExpBuffer exp({ 0x00, 0x00, 0x00, 0x02, 0x01, 0x23, 0x45, 0x67 }); - f.assertSerialize(exp, val); -} - TEST_F("Test serializing std::vector", Fixture) { std::vector<int16_t> val({ 0x0123, 0x4567 }); diff --git a/vespalib/src/vespa/vespalib/coro/CMakeLists.txt b/vespalib/src/vespa/vespalib/coro/CMakeLists.txt index 8a7a0ade049..ed30f224eab 100644 --- a/vespalib/src/vespa/vespalib/coro/CMakeLists.txt +++ b/vespalib/src/vespa/vespalib/coro/CMakeLists.txt @@ -1,6 +1,7 @@ # Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. vespa_add_library(vespalib_vespalib_coro OBJECT SOURCES + active_work.cpp async_crypto_socket.cpp async_io.cpp DEPENDS diff --git a/vespalib/src/vespa/vespalib/coro/active_work.cpp b/vespalib/src/vespa/vespalib/coro/active_work.cpp new file mode 100644 index 00000000000..403da5173fe --- /dev/null +++ b/vespalib/src/vespa/vespalib/coro/active_work.cpp @@ -0,0 +1,21 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "active_work.h" +#include <cassert> + +namespace vespalib::coro { + +bool +ActiveWork::join_awaiter::await_suspend(std::coroutine_handle<> handle) noexcept +{ + self._waiting = handle; + return (self._pending.fetch_sub(1, std::memory_order_acq_rel) > 1); +} + +ActiveWork::~ActiveWork() +{ + // NB: join must be called, even if there is no other work + assert(_pending.load(std::memory_order_relaxed) == 0); +} + +} diff --git a/vespalib/src/vespa/vespalib/coro/active_work.h b/vespalib/src/vespa/vespalib/coro/active_work.h new file mode 100644 index 00000000000..9f4079615c7 --- /dev/null +++ b/vespalib/src/vespa/vespalib/coro/active_work.h @@ -0,0 +1,42 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include "lazy.h" +#include "detached.h" +#include <coroutine> +#include <atomic> + +namespace vespalib::coro { + +// Tracks work that is being performed concurrently +class ActiveWork { +private: + std::atomic<uint32_t> _pending; + std::coroutine_handle<> _waiting; + template <typename T> + Detached signal_when_done(Lazy<T> &lazy) { + co_await lazy.done(); + if (_pending.fetch_sub(1, std::memory_order_acq_rel) == 1) { + _waiting.resume(); + } + } + struct join_awaiter { + ActiveWork &self; + join_awaiter(ActiveWork &self_in) noexcept : self(self_in) {} + constexpr bool await_ready() const noexcept { return false; } + constexpr void await_resume() const noexcept {} + bool await_suspend(std::coroutine_handle<> handle) noexcept __attribute__((noinline)); + }; +public: + ActiveWork() : _pending(1), _waiting(std::noop_coroutine()) {} + ~ActiveWork(); + template <typename T> + void start(Lazy<T> &lazy) { + _pending.fetch_add(1, std::memory_order_relaxed); + signal_when_done(lazy); + } + auto join() noexcept { return join_awaiter(*this); } +}; + +} diff --git a/vespalib/src/vespa/vespalib/coro/lazy.h b/vespalib/src/vespa/vespalib/coro/lazy.h index 17077dccc9f..87abb2e4f99 100644 --- a/vespalib/src/vespa/vespalib/coro/lazy.h +++ b/vespalib/src/vespa/vespalib/coro/lazy.h @@ -75,6 +75,9 @@ private: struct Result { static Received<T>&& get(auto &&promise) { return std::move(promise.result); } }; + struct Nothing { + static void get(auto &&) {} + }; public: Lazy(const Lazy &) = delete; @@ -84,6 +87,7 @@ public: auto operator co_await() & noexcept { return WaitFor<LValue>(_handle); } auto operator co_await() && noexcept { return WaitFor<RValue>(_handle); } auto forward() noexcept { return WaitFor<Result>(_handle); } + auto done() noexcept { return WaitFor<Nothing>(_handle); } ~Lazy() { if (_handle) { _handle.destroy(); diff --git a/vespalib/src/vespa/vespalib/objects/deserializer.h b/vespalib/src/vespa/vespalib/objects/deserializer.h index e59d3e07581..dcb1b5176cb 100644 --- a/vespalib/src/vespa/vespalib/objects/deserializer.h +++ b/vespalib/src/vespa/vespalib/objects/deserializer.h @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once -#include <vespa/vespalib/util/array.h> #include <vespa/vespalib/stllike/string.h> #include <vector> #include <cstdint> @@ -44,11 +43,7 @@ public: Deserializer & operator >> (double & value) { return get(value); } Deserializer & operator >> (string & value) { return get(value); } template <typename T> - Deserializer & operator >> (vespalib::Array<T> & v); - template <typename T> Deserializer & operator >> (std::vector<T> & v); - }; } - diff --git a/vespalib/src/vespa/vespalib/objects/deserializer.hpp b/vespalib/src/vespa/vespalib/objects/deserializer.hpp index b90867fa153..cf9bd5b0a48 100644 --- a/vespalib/src/vespa/vespalib/objects/deserializer.hpp +++ b/vespalib/src/vespa/vespalib/objects/deserializer.hpp @@ -7,18 +7,6 @@ namespace vespalib { template <typename T> Deserializer & -Deserializer::operator >> (vespalib::Array<T> & v) { - uint32_t sz; - get(sz); - v.resize(sz); - for(size_t i(0); i < sz; i++) { - (*this) >> v[i]; - } - return *this; -} - -template <typename T> -Deserializer & Deserializer::operator >> (std::vector<T> & v) { uint32_t sz; get(sz); diff --git a/vespalib/src/vespa/vespalib/objects/nbostream.h b/vespalib/src/vespa/vespalib/objects/nbostream.h index 12d4fac04cc..bf2adc7b486 100644 --- a/vespalib/src/vespa/vespalib/objects/nbostream.h +++ b/vespalib/src/vespa/vespalib/objects/nbostream.h @@ -87,25 +87,6 @@ public: return *this; } template <typename T> - nbostream & operator << (const vespalib::Array<T> & v) { - uint32_t sz(v.size()); - (*this) << sz; - for(size_t i(0); i < sz; i++) { - (*this) << v[i]; - } - return *this; - } - template <typename T> - nbostream & operator >> (vespalib::Array<T> & v) { - uint32_t sz; - (*this) >> sz; - v.resize(sz); - for(size_t i(0); i < sz; i++) { - (*this) >> v[i]; - } - return *this; - } - template <typename T> nbostream & operator << (const std::vector<T> & v) { uint32_t sz(v.size()); (*this) << sz; @@ -175,8 +156,6 @@ public: } void swap(Buffer & buf); void swap(nbostream & os); - /** extract the underlying Array<char>; nbostream will be empty afterwards. */ - Buffer extract_buffer() { Buffer rv; swap(rv); return rv; } /** * This flag can be used to tell that a buffer will live at least as long as * any objects it will be the backing for. In those cases there is no need for diff --git a/vespalib/src/vespa/vespalib/objects/serializer.h b/vespalib/src/vespa/vespalib/objects/serializer.h index f10d03b1c04..f8a43ab104a 100644 --- a/vespalib/src/vespa/vespalib/objects/serializer.h +++ b/vespalib/src/vespa/vespalib/objects/serializer.h @@ -1,7 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once -#include <vespa/vespalib/util/array.h> #include <vespa/vespalib/stllike/string.h> #include <vector> #include <cstdint> @@ -42,10 +41,7 @@ public: Serializer & operator << (double value) { return put(value); } Serializer & operator << (stringref value) { return put(value); } template <typename T> - Serializer & operator << (const vespalib::Array<T> & v); - template <typename T> Serializer & operator << (const std::vector<T> & v); }; } - diff --git a/vespalib/src/vespa/vespalib/objects/serializer.hpp b/vespalib/src/vespa/vespalib/objects/serializer.hpp index 87c02ddf693..1962ddd4359 100644 --- a/vespalib/src/vespa/vespalib/objects/serializer.hpp +++ b/vespalib/src/vespa/vespalib/objects/serializer.hpp @@ -9,16 +9,6 @@ namespace vespalib { template <typename T> Serializer & -Serializer::operator << (const vespalib::Array<T> & v) { - uint32_t sz(v.size()); - put(sz); - for(size_t i(0); i < sz; i++) { - (*this) << v[i]; - } - return *this; -} -template <typename T> -Serializer & Serializer::operator << (const std::vector<T> & v) { uint32_t sz(v.size()); put(sz); diff --git a/vespalib/src/vespa/vespalib/objects/visit.hpp b/vespalib/src/vespa/vespalib/objects/visit.hpp index e3a82b212c0..9838e331823 100644 --- a/vespalib/src/vespa/vespalib/objects/visit.hpp +++ b/vespalib/src/vespa/vespalib/objects/visit.hpp @@ -2,9 +2,9 @@ #pragma once #include "visit.h" -#include <vector> -#include <vespa/vespalib/util/stringfmt.h> #include "objectvisitor.h" +#include <vespa/vespalib/util/stringfmt.h> +#include <vespa/vespalib/util/array.h> #include "identifiable.hpp" template<typename T> diff --git a/vespalib/src/vespa/vespalib/stllike/cache.h b/vespalib/src/vespa/vespalib/stllike/cache.h index 907fbdd54c9..f7456cda197 100644 --- a/vespalib/src/vespa/vespalib/stllike/cache.h +++ b/vespalib/src/vespa/vespalib/stllike/cache.h @@ -69,10 +69,6 @@ public: * Can be used for controlling max number of elements. */ cache & maxElements(size_t elems); - /** - * Can be used for reserving space for elements. - */ - cache & reserveElements(size_t elems); cache & setCapacityBytes(size_t sz); diff --git a/vespalib/src/vespa/vespalib/stllike/cache.hpp b/vespalib/src/vespa/vespalib/stllike/cache.hpp index 8e449fcfca4..4e7736c9e5f 100644 --- a/vespalib/src/vespa/vespalib/stllike/cache.hpp +++ b/vespalib/src/vespa/vespalib/stllike/cache.hpp @@ -16,13 +16,6 @@ cache<P>::maxElements(size_t elems) { template< typename P > cache<P> & -cache<P>::reserveElements(size_t elems) { - Lru::reserve(elems); - return *this; -} - -template< typename P > -cache<P> & cache<P>::setCapacityBytes(size_t sz) { _maxBytes.store(sz, std::memory_order_relaxed); return *this; diff --git a/vespalib/src/vespa/vespalib/util/array.h b/vespalib/src/vespa/vespalib/util/array.h index e9d8c9ccfdb..7b23f6245c7 100644 --- a/vespalib/src/vespa/vespalib/util/array.h +++ b/vespalib/src/vespa/vespalib/util/array.h @@ -16,77 +16,76 @@ class Array { public: class reverse_iterator { public: - reverse_iterator() : _p(NULL) { } - reverse_iterator(T * p) : _p(p) { } - T & operator *() { return _p[0]; } - T * operator -> () { return _p; } - reverse_iterator operator -(size_t n) { return _p + n; } - reverse_iterator operator +(size_t n) { return _p - n; } - reverse_iterator & operator ++ () { + reverse_iterator() noexcept : _p(nullptr) { } + reverse_iterator(T * p) noexcept : _p(p) { } + T & operator *() noexcept { return _p[0]; } + T * operator -> () noexcept { return _p; } + reverse_iterator operator -(size_t n) noexcept { return _p + n; } + reverse_iterator operator +(size_t n) noexcept { return _p - n; } + reverse_iterator & operator ++ () noexcept { _p--; return *this; } - reverse_iterator operator ++ (int) { + reverse_iterator operator ++ (int) noexcept { reverse_iterator prev = *this; _p--; return prev; } - reverse_iterator & operator -- () { + reverse_iterator & operator -- () noexcept { _p++; return *this; } - reverse_iterator operator -- (int) { + reverse_iterator operator -- (int) noexcept { reverse_iterator prev = *this; _p++; return prev; } - T * get() { return _p; } + T * get() noexcept { return _p; } private: - friend size_t operator -(reverse_iterator a, reverse_iterator b) { return b._p - a._p; } + friend size_t operator -(reverse_iterator a, reverse_iterator b) noexcept { return b._p - a._p; } T * _p; }; class const_reverse_iterator { public: - const_reverse_iterator() : _p(NULL) { } - const_reverse_iterator(const T * p) : _p(p) { } - const_reverse_iterator(reverse_iterator i) : _p(i.get()) { } - const T & operator *() const { return _p[0]; } - const T * operator -> () const { return _p; } - const_reverse_iterator operator -(size_t n) { return _p + n; } - const_reverse_iterator operator +(size_t n) { return _p - n; } - const_reverse_iterator & operator ++ () { + const_reverse_iterator() noexcept : _p(nullptr) { } + const_reverse_iterator(const T * p) noexcept : _p(p) { } + const_reverse_iterator(reverse_iterator i) noexcept : _p(i.get()) { } + const T & operator *() const noexcept { return _p[0]; } + const T * operator -> () const noexcept { return _p; } + const_reverse_iterator operator -(size_t n) noexcept { return _p + n; } + const_reverse_iterator operator +(size_t n) noexcept { return _p - n; } + const_reverse_iterator & operator ++ () noexcept { _p--; return *this; } - const_reverse_iterator operator ++ (int) { + const_reverse_iterator operator ++ (int) noexcept { const_reverse_iterator prev = *this; _p--; return prev; } - const_reverse_iterator & operator -- () { + const_reverse_iterator & operator -- () noexcept { _p++; return *this; } - const_reverse_iterator operator -- (int) { + const_reverse_iterator operator -- (int) noexcept { const_reverse_iterator prev = *this; _p++; return prev; } private: - friend size_t operator -(const_reverse_iterator a, const_reverse_iterator b) { return b._p - a._p; } + friend size_t operator -(const_reverse_iterator a, const_reverse_iterator b) noexcept { return b._p - a._p; } const T * _p; }; using Alloc = alloc::Alloc; using const_iterator = const T *; using iterator = T *; - using const_reference = const T &; using value_type = T; using size_type = size_t; Array(const Alloc & initial=Alloc::alloc()); Array(size_t sz, const Alloc & initial=Alloc::alloc()); - Array(Alloc && buf, size_t sz); + Array(Alloc && buf, size_t sz) noexcept; Array(Array &&rhs) noexcept; Array(size_t sz, T value, const Alloc & initial=Alloc::alloc()); Array(const_iterator begin, const_iterator end, const Alloc & initial=Alloc::alloc()); @@ -94,7 +93,7 @@ public: Array & operator =(const Array & rhs); Array & operator =(Array && rhs) noexcept; ~Array(); - void swap(Array & rhs) { + void swap(Array & rhs) noexcept { _array.swap(rhs._array); std::swap(_sz, rhs._sz); } @@ -117,39 +116,39 @@ public: std::destroy_at(array(_sz)); } - T & back() { return *array(_sz-1); } - const T & back() const { return *array(_sz-1); } - const_iterator begin() const { return array(0); } - const_iterator end() const { return array(_sz); } - iterator begin() { return array(0); } - iterator end() { return array(_sz); } - const_reverse_iterator rbegin() const { return empty() ? array(0) : array(_sz) - 1; } - const_reverse_iterator rend() const { return empty() ? array(0) : array(0) - 1; } - reverse_iterator rbegin() { return empty() ? array(0) : array(_sz) - 1; } - reverse_iterator rend() { return empty() ? array(0) : array(0) - 1; } - size_t size() const { return _sz; } - size_t capacity() const { return _array.size()/sizeof(T); } + T & back() noexcept { return *array(_sz-1); } + const T & back() const noexcept { return *array(_sz-1); } + const_iterator begin() const noexcept { return array(0); } + const_iterator end() const noexcept { return array(_sz); } + iterator begin() noexcept { return array(0); } + iterator end() noexcept { return array(_sz); } + const_reverse_iterator rbegin() const noexcept { return empty() ? array(0) : array(_sz) - 1; } + const_reverse_iterator rend() const noexcept { return empty() ? array(0) : array(0) - 1; } + reverse_iterator rbegin() noexcept { return empty() ? array(0) : array(_sz) - 1; } + reverse_iterator rend() noexcept { return empty() ? array(0) : array(0) - 1; } + size_t size() const noexcept { return _sz; } + size_t capacity() const noexcept { return _array.size()/sizeof(T); } void clear() { std::destroy(array(0), array(_sz)); _sz = 0; } void reset(); - bool empty() const { return _sz == 0; } - T * data() noexcept { return static_cast<T *>(_array.get()); } - const T * data() const noexcept { return static_cast<const T *>(_array.get()); } - T & operator [] (size_t i) { return *array(i); } - const T & operator [] (size_t i) const { return *array(i); } - bool operator == (const Array & rhs) const; - bool operator != (const Array & rhs) const; + bool empty() const noexcept { return _sz == 0; } + T * data() noexcept { return static_cast<T *>(_array.get()); } + const T * data() const noexcept { return static_cast<const T *>(_array.get()); } + T & operator [] (size_t i) noexcept { return *array(i); } + const T & operator [] (size_t i) const noexcept { return *array(i); } + bool operator == (const Array & rhs) const noexcept; + bool operator != (const Array & rhs) const noexcept; - static Alloc stealAlloc(Array && rhs) { + static Alloc stealAlloc(Array && rhs) noexcept { rhs._sz = 0; return std::move(rhs._array); } Array<T> create() const; private: - T * array(size_t i) { return static_cast<T *>(_array.get()) + i; } - const T * array(size_t i) const { return static_cast<const T *>(_array.get()) + i; } + T * array(size_t i) noexcept { return static_cast<T *>(_array.get()) + i; } + const T * array(size_t i) const noexcept { return static_cast<const T *>(_array.get()) + i; } void cleanup(); void increase(size_t n); void extend(size_t n) { diff --git a/vespalib/src/vespa/vespalib/util/array.hpp b/vespalib/src/vespa/vespalib/util/array.hpp index 24136e544b8..a68aca1ddb0 100644 --- a/vespalib/src/vespa/vespalib/util/array.hpp +++ b/vespalib/src/vespa/vespalib/util/array.hpp @@ -17,7 +17,7 @@ void construct(T * dest, const T * source, size_t sz, std::false_type) } template <typename T> -void construct(T * dest, const T * source, size_t sz, std::true_type) +void construct(T * dest, const T * source, size_t sz, std::true_type) noexcept { memcpy(dest, source, sz*sizeof(T)); } @@ -32,7 +32,7 @@ void construct(T * dest, size_t sz, std::false_type) } template <typename T> -void construct(T * dest, size_t sz, std::true_type) +void construct(T * dest, size_t sz, std::true_type) noexcept { (void) dest; (void) sz; @@ -123,7 +123,7 @@ void Array<T>::resize(size_t n) } template <typename T> -void move(T * dest, T * source, size_t sz, std::false_type) +void move(T * dest, T * source, size_t sz, std::false_type) noexcept { for (size_t i(0); i < sz; i++) { ::new (static_cast<void *>(dest + i)) T(std::move(*(source + i))); @@ -132,7 +132,7 @@ void move(T * dest, T * source, size_t sz, std::false_type) } template <typename T> -void move(T * dest, const T * source, size_t sz, std::true_type) +void move(T * dest, const T * source, size_t sz, std::true_type) noexcept { memcpy(dest, source, sz*sizeof(T)); } @@ -154,7 +154,7 @@ Array<T>::Array(const Alloc & initial) { } template <typename T> -Array<T>::Array(Alloc && buf, size_t sz) : +Array<T>::Array(Alloc && buf, size_t sz) noexcept : _array(std::move(buf)), _sz(sz) { diff --git a/vespalib/src/vespa/vespalib/util/array_equal.hpp b/vespalib/src/vespa/vespalib/util/array_equal.hpp index 3b3aab6280b..3e7e624b6e9 100644 --- a/vespalib/src/vespa/vespalib/util/array_equal.hpp +++ b/vespalib/src/vespa/vespalib/util/array_equal.hpp @@ -6,7 +6,7 @@ namespace vespalib { template <typename T> -bool Array<T>::operator ==(const Array & rhs) const +bool Array<T>::operator ==(const Array & rhs) const noexcept { bool retval(size() == rhs.size()); for (size_t i(0); retval && (i < _sz); i++) { @@ -18,7 +18,7 @@ bool Array<T>::operator ==(const Array & rhs) const } template <typename T> -bool Array<T>::operator != (const Array & rhs) const { +bool Array<T>::operator != (const Array & rhs) const noexcept { return !(*this == rhs); } |