diff options
author | Sven Gothel <[email protected]> | 2021-01-07 09:08:03 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2021-01-07 09:08:03 +0100 |
commit | a2a21c4be8673f2c6ec603f37ce53fb5237f727d (patch) | |
tree | 8d1e68c7d7bdb92a1703ddab64571ab4b7249c12 /test | |
parent | 95c6d559f66ea462046d4c1663b228461822341d (diff) |
cow_darray: Fail safe API: Only [c]begin() iterator retrieval, leave other operations on the cow_[ro|rw]_iterator avoding data races
These changes have been adopted to cow_vector as well, still deprecated now.
To allow data-race free operations using iterators from a potentially mutated CoW,
only one cow_darray::begin() const_iterator or iterator should be retrieved from this CoW
and all further operations shall use its
jau::cow_ro_iterator::size(), jau::cow_ro_iterator::begin() and jau::cow_ro_iterator::end()
- or its respective variant from jau::cow_rw_iterator.
Removed from cow_darray:
- 'const_iterator begin() const' // ambiguous mutable/immutable overload
- 'const_iterator cend()' // fetch from cow_ro_iterator::end()
- 'iterator end()' // fetch from cow_rw_iterator::end()
cow_[ro|rw]_iterator:
- private constructor w/ fried to cow_darray, .. only allow copy-ctor etc, not allowing to set false pointer
- Added size(), begin() and end(): Allowing data-race free operations
- full typedefs
- Holds the 'begin iterator' internally to implement begin() and end()
+++
Added new convenience template Type Traits and functions:
- 'template< class T > is_cow_type<T>::value' compile-time Type Trait,
determining whether the given template class is a CoW type, e.g. jau::cow_darray,
jau::cow_vector or any of their iterator.
For both, cow_type or any other using above 'is_cow_type<T>' trait:
- find_const(T& data)
- for_each_const(T& data)
- for_each_fidelity(T& data)
++++
Performance: No regression, even a little more speed-up.
Diffstat (limited to 'test')
-rw-r--r-- | test/test_cow_darray_01.cpp | 34 | ||||
-rw-r--r-- | test/test_cow_darray_perf01.cpp | 50 | ||||
-rw-r--r-- | test/test_cow_iterator_01.cpp | 125 | ||||
-rw-r--r-- | test/test_hashset_perf01.cpp | 47 |
4 files changed, 120 insertions, 136 deletions
diff --git a/test/test_cow_darray_01.cpp b/test/test_cow_darray_01.cpp index 7b7f87a..1ae6fb8 100644 --- a/test/test_cow_darray_01.cpp +++ b/test/test_cow_darray_01.cpp @@ -35,6 +35,7 @@ #include "test_datatype01.hpp" +#include <jau/basic_algos.hpp> #include <jau/basic_types.hpp> #include <jau/darray.hpp> #include <jau/cow_darray.hpp> @@ -72,17 +73,6 @@ DataType01 * findDataSet01_idx(T& data, DataType01 const & elem) noexcept { } return nullptr; } -template<class T> -const DataType01 * findDataSet01_itr(T& data, DataType01 const & elem) noexcept { - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - if( elem == *iter ) { - return &(*iter); - } - } - return nullptr; -} template<class T> static void test_00_list_idx(T& data, const bool show) { @@ -98,18 +88,20 @@ static void test_00_list_idx(T& data, const bool show) { } } template<class T> -static void test_00_list_itr(T& data, const bool show) { +static int test_00_list_itr(T& data, const bool show) { Addr48Bit a0(start_addr); - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(std::size_t i = 0; iter != end && a0.next(); ++iter, ++i) { - const DataType01 & e = *iter; - e.nop(); + int some_number = 0, i=0; // add some validated work, avoiding any 'optimization away' + jau::for_each_const(data, [&some_number, &a0, &i, show](const DataType01 & e) { + some_number += e.nop(); if( show ) { - printf("data[%zu]: %s\n", i, e.toString().c_str()); + printf("data[%d]: %s\n", i, e.toString().c_str()); } + REQUIRE( a0.next() ); REQUIRE(e.address == a0); - } + ++i; + } ); + REQUIRE(some_number > 0); + return some_number; } template<class T> @@ -136,7 +128,7 @@ static void test_00_seq_find_itr(T& data) { for(; i<size && a0.next(); i++) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01 *found = findDataSet01_itr(data, elem); + const DataType01 *found = jau::find_const(data, elem); if( nullptr != found ) { fi++; found->nop(); @@ -191,7 +183,7 @@ static void test_00_seq_fill_unique_itr(T& data, const std::size_t size) { for(; i<size && a0.next(); i++) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01* exist = findDataSet01_itr(data, elem); + const DataType01* exist = jau::find_const(data, elem); if( nullptr == exist ) { data.push_back( std::move(elem) ); fi++; diff --git a/test/test_cow_darray_perf01.cpp b/test/test_cow_darray_perf01.cpp index c4c519b..70e3a47 100644 --- a/test/test_cow_darray_perf01.cpp +++ b/test/test_cow_darray_perf01.cpp @@ -52,9 +52,6 @@ using namespace jau; static uint8_t start_addr_b[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static Addr48Bit start_addr(start_addr_b); -// #define USE_STD_ITER_ALGO 1 -#define USE_JAU_ITER_ALGO 1 - /**************************************************************************************** ****************************************************************************************/ @@ -70,34 +67,6 @@ DataType01 * findDataSet01_idx(T& data, DataType01 const & elem) noexcept { return nullptr; } -template<class T> -const DataType01 * findDataSet01_itr(T& data, DataType01 const & elem) noexcept { -#if defined(USE_STD_ITER_ALGO) - // much slower, approx 3x over 1000 * 1000, why? - typename T::const_iterator end = data.cend(); - auto it = std::find( data.cbegin(), end, elem); - if( it != end ) { - return &(*it); - } -#elif defined (USE_JAU_ITER_ALGO) - // same logic, much faster - typename T::const_iterator end = data.cend(); - auto it = jau::find( data.cbegin(), end, elem); - if( it != end ) { - return &(*it); - } -#else - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - if( elem == *iter ) { - return &(*iter); - } - } -#endif - return nullptr; -} - template<class T, typename Size_type> static int test_00_list_idx(T& data) { int some_number = 0; // add some validated work, avoiding any 'optimization away' @@ -113,20 +82,9 @@ static int test_00_list_idx(T& data) { template<class T> static int test_00_list_itr(T& data) { int some_number = 0; // add some validated work, avoiding any 'optimization away' -#if defined(USE_STD_ITER_ALGO) - // slower, why? - std::for_each(data.cbegin(), data.cend(), [&some_number](const DataType01 & e) { some_number += e.nop(); }); -#elif defined (USE_JAU_ITER_ALGO) - // same logic, faster - jau::for_each(data.cbegin(), data.cend(), [&some_number](const DataType01 & e) { some_number += e.nop(); }); -#else - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - const DataType01 & e = *iter; + jau::for_each_const(data, [&some_number](const DataType01 & e) { some_number += e.nop(); - } -#endif + } ); REQUIRE(some_number > 0); return some_number; } @@ -156,7 +114,7 @@ static void test_00_seq_find_itr(T& data) { for(; i<size && a0.next(); ++i) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01 *found = findDataSet01_itr<T>(data, elem); + const DataType01 *found = jau::find_const<T>(data, elem); if( nullptr != found ) { ++fi; found->nop(); @@ -199,7 +157,7 @@ static void test_00_seq_fill_unique_itr(T& data, const Size_type size) { for(; i<size && a0.next(); ++i) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01* exist = findDataSet01_itr<T>(data, elem); + const DataType01* exist = jau::find_const<T>(data, elem); if( nullptr == exist ) { data.push_back( std::move( elem ) ); ++fi; diff --git a/test/test_cow_iterator_01.cpp b/test/test_cow_iterator_01.cpp index 737d870..834a4c7 100644 --- a/test/test_cow_iterator_01.cpp +++ b/test/test_cow_iterator_01.cpp @@ -36,6 +36,7 @@ #include "test_datatype01.hpp" +#include <jau/basic_algos.hpp> #include <jau/basic_types.hpp> #include <jau/darray.hpp> #include <jau/cow_darray.hpp> @@ -67,30 +68,20 @@ JAU_TYPENAME_CUE_ALL(jau_cow_vector_DataType01) JAU_TYPENAME_CUE_ALL(jau_cow_darray_DataType01) template<class T> -const DataType01 * findDataSet01_itr(T& data, DataType01 const & elem) noexcept { - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - if( elem == *iter ) { - return &(*iter); - } - } - return nullptr; -} - -template<class T> -static void test_00_list_itr(T& data, const bool show) { +static int test_00_list_itr(T& data, const bool show) { Addr48Bit a0(start_addr); - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(std::size_t i = 0; iter != end && a0.next(); ++iter, ++i) { - const DataType01 & e = *iter; - e.nop(); + int some_number = 0, i=0; // add some validated work, avoiding any 'optimization away' + jau::for_each_const(data, [&some_number, &a0, &i, show](const DataType01 & e) { + some_number += e.nop(); if( show ) { - printf("data[%zu]: %s\n", i, e.toString().c_str()); + printf("data[%d]: %s\n", i, e.toString().c_str()); } + REQUIRE( a0.next() ); REQUIRE(e.address == a0); - } + ++i; + } ); + REQUIRE(some_number > 0); + return some_number; } template<class T> @@ -101,7 +92,7 @@ static void test_00_seq_find_itr(T& data) { for(; i<size && a0.next(); i++) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01 *found = findDataSet01_itr(data, elem); + const DataType01 *found = jau::find_const(data, elem); if( nullptr != found ) { fi++; found->nop(); @@ -419,11 +410,56 @@ static void test_iterator_arithmetic(const typename T::size_type size, } template<class T> -static bool test_const_iterator_ops(const std::string& type_id, T& data) { - printf("**** test_const_iterator_ops: %s\n", type_id.c_str()); +static bool test_const_iterator_ops(const std::string& type_id, T& data, + std::enable_if_t< is_cow_type<T>::value, bool> = true ) +{ + printf("**** test_const_iterator_ops(CoW): %s\n", type_id.c_str()); + { + typename T::const_iterator begin = data.cbegin(); // immutable new_store non-const iterator, gets held until destruction + typename T::const_iterator end = begin.end(); // no new store iterator, on same store as begin, obtained from begin + typename T::difference_type data_size = static_cast<typename T::difference_type>(data.size()); + typename T::difference_type begin_size = static_cast<typename T::difference_type>(begin.size()); + typename T::difference_type end_size = static_cast<typename T::difference_type>(end.size()); + REQUIRE( begin_size == data_size ); + REQUIRE( end_size == data_size ); + REQUIRE( end - begin == data_size ); + REQUIRE( end - end_size == begin ); + REQUIRE( begin + begin_size == end ); + REQUIRE( *( end - end_size ) == *begin ); + REQUIRE( *( begin + begin_size ) == *end ); + test_iterator_dereference<T, typename T::const_iterator>(begin.size(), begin, end); + } + { typename T::const_iterator begin = data.cbegin(); // no new store const_iterator - typename T::const_iterator end = data.cend(); // no new store const_iterator, on same store as begin + typename T::const_iterator end = begin.end(); // no new store const_iterator, on same store as begin, obtained from begin + test_iterator_arithmetic<T, typename T::const_iterator>(data.size(), begin, end); + } +#if 0 + { + // INTENIONAL FAILURES, checking behavior of error values etc + typename T::const_iterator begin = data.cbegin(); // no new store const_iterator + typename T::const_iterator iter = begin + 1; + CHECK( *begin == *iter ); + CHECK( begin == iter ); + } +#endif + return true; +} +template<class T> +static bool test_const_iterator_ops(const std::string& type_id, T& data, + std::enable_if_t< !is_cow_type<T>::value, bool> = true ) +{ + printf("**** test_const_iterator_ops: %s\n", type_id.c_str()); + { + typename T::const_iterator begin = data.cbegin(); // mutable new_store non-const iterator, gets held until destruction + typename T::const_iterator end = data.cend(); // no new store iterator, on same store as begin and from begin + typename T::difference_type data_size = static_cast<typename T::difference_type>(data.size()); + REQUIRE( end - begin == data_size ); + REQUIRE( end - data_size == begin ); + REQUIRE( begin + data_size == end ); + REQUIRE( *( end - data_size ) == *begin ); + REQUIRE( *( begin + data_size ) == *end ); test_iterator_dereference<T, typename T::const_iterator>(data.size(), begin, end); } @@ -445,11 +481,48 @@ static bool test_const_iterator_ops(const std::string& type_id, T& data) { } template<class T> -static bool test_mutable_iterator_ops(const std::string& type_id, T& data) { - printf("**** test_mutable_iterator_ops: %s\n", type_id.c_str()); +static bool test_mutable_iterator_ops(const std::string& type_id, T& data, + std::enable_if_t< is_cow_type<T>::value, bool> = true ) +{ + printf("**** test_mutable_iterator_ops(CoW): %s\n", type_id.c_str()); + { + typename T::iterator begin = data.begin(); // mutable new_store non-const iterator, gets held until destruction + typename T::iterator end = begin.end(); // no new store iterator, on same store as begin and from begin + typename T::difference_type data_size = static_cast<typename T::difference_type>(data.size()); + typename T::difference_type begin_size = static_cast<typename T::difference_type>(begin.size()); + typename T::difference_type end_size = static_cast<typename T::difference_type>(end.size()); + REQUIRE( begin_size == data_size ); + REQUIRE( end_size == data_size ); + REQUIRE( end - begin == data_size ); + REQUIRE( end - end_size == begin ); + REQUIRE( begin + begin_size == end ); + REQUIRE( *( end - end_size ) == *begin ); + REQUIRE( *( begin + begin_size ) == *end ); + test_iterator_dereference<T, typename T::iterator>(begin.size(), begin, end); + } + { typename T::iterator begin = data.begin(); // mutable new_store non-const iterator, gets held until destruction typename T::iterator end = begin + data.size(); // hence use iterator artihmetic to have end pointer + test_iterator_arithmetic<T, typename T::iterator>(data.size(), begin, end); + } + return true; +} + +template<class T> +static bool test_mutable_iterator_ops(const std::string& type_id, T& data, + std::enable_if_t< !is_cow_type<T>::value, bool> = true ) +{ + printf("**** test_mutable_iterator_ops(___): %s\n", type_id.c_str()); + { + typename T::iterator begin = data.begin(); // mutable new_store non-const iterator, gets held until destruction + typename T::iterator end = data.end(); // no new store iterator, on same store as begin and from begin + typename T::difference_type data_size = static_cast<typename T::difference_type>(data.size()); + REQUIRE( end - begin == data_size ); + REQUIRE( end - data_size == begin ); + REQUIRE( begin + data_size == end ); + REQUIRE( *( end - data_size ) == *begin ); + REQUIRE( *( begin + data_size ) == *end ); test_iterator_dereference<T, typename T::iterator>(data.size(), begin, end); } diff --git a/test/test_hashset_perf01.cpp b/test/test_hashset_perf01.cpp index ff8c8d2..223c05d 100644 --- a/test/test_hashset_perf01.cpp +++ b/test/test_hashset_perf01.cpp @@ -54,34 +54,6 @@ static Addr48Bit start_addr(start_addr_b); #define USE_JAU_ITER_ALGO 1 template<class T> -const DataType01 * findDataSet01_itr(T& data, DataType01 const & elem) noexcept { -#if defined(USE_STD_ITER_ALGO) - // much slower, approx 3x over 1000 * 1000, why? - typename T::const_iterator end = data.cend(); - auto it = std::find( data.cbegin(), end, elem); - if( it != end ) { - return &(*it); - } -#elif defined (USE_JAU_ITER_ALGO) - // same logic, much faster - typename T::const_iterator end = data.cend(); - auto it = jau::find( data.cbegin(), end, elem); - if( it != end ) { - return &(*it); - } -#else - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - if( elem == *iter ) { - return &(*iter); - } - } -#endif - return nullptr; -} - -template<class T> const DataType01 * findDataSet01_hash(T& data, DataType01 const & elem) noexcept { auto search = data.find(elem); if( search != data.end() ) { @@ -93,20 +65,9 @@ const DataType01 * findDataSet01_hash(T& data, DataType01 const & elem) noexcept template<class T> static int test_00_list_itr(T& data) { int some_number = 0; // add some validated work, avoiding any 'optimization away' -#if defined(USE_STD_ITER_ALGO) - // slower, why? - std::for_each(data.cbegin(), data.cend(), [&some_number](const DataType01 & e) { some_number += e.nop(); }); -#elif defined (USE_JAU_ITER_ALGO) - // same logic, faster - jau::for_each(data.cbegin(), data.cend(), [&some_number](const DataType01 & e) { some_number += e.nop(); }); -#else - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - const DataType01 & e = *iter; + jau::for_each_const(data, [&some_number](const DataType01 & e) { some_number += e.nop(); - } -#endif + } ); REQUIRE(some_number > 0); return some_number; } @@ -119,7 +80,7 @@ static void test_00_seq_find_itr(T& data) { for(; i<size && a0.next(); ++i) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01 *found = findDataSet01_itr<T>(data, elem); + const DataType01 *found = jau::find_const<T>(data, elem); if( nullptr != found ) { ++fi; found->nop(); @@ -163,7 +124,7 @@ static void test_00_seq_fill_unique_itr(T& data, const Size_type size) { for(; i<size && a0.next(); ++i) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01* exist = findDataSet01_itr<T>(data, elem); + const DataType01* exist = jau::find_const<T>(data, elem); if( nullptr == exist ) { data.push_back( std::move( elem ) ); ++fi; |