aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2021-01-07 09:08:03 +0100
committerSven Gothel <[email protected]>2021-01-07 09:08:03 +0100
commita2a21c4be8673f2c6ec603f37ce53fb5237f727d (patch)
tree8d1e68c7d7bdb92a1703ddab64571ab4b7249c12 /test
parent95c6d559f66ea462046d4c1663b228461822341d (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.cpp34
-rw-r--r--test/test_cow_darray_perf01.cpp50
-rw-r--r--test/test_cow_iterator_01.cpp125
-rw-r--r--test/test_hashset_perf01.cpp47
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;