Direct-BT  2.3.1
Direct-BT - Direct Bluetooth Programming.
test_hashset_perf01.cpp
Go to the documentation of this file.
1 /*
2  * Author: Sven Gothel <sgothel@jausoft.com>
3  * Copyright (c) 2020 Gothel Software e.K.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include <iostream>
25 #include <cassert>
26 #include <cinttypes>
27 #include <cstring>
28 #include <random>
29 #include <vector>
30 #include <unordered_set>
31 
32 #define CATCH_CONFIG_RUNNER
33 // #define CATCH_CONFIG_MAIN
34 #include <catch2/catch_amalgamated.hpp>
35 #include <jau/test/catch2_ext.hpp>
36 
37 #include "test_datatype01.hpp"
38 
39 #include <jau/basic_types.hpp>
40 #include <jau/basic_algos.hpp>
42 #include <jau/callocator.hpp>
44 #include <jau/darray.hpp>
45 #include <jau/cow_darray.hpp>
46 #include <jau/cow_vector.hpp>
47 
48 using namespace jau;
49 
50 static uint8_t start_addr_b[] = {0x20, 0x26, 0x2A, 0x01, 0x20, 0x10};
52 
53 // #define USE_STD_ITER_ALGO 1
54 #define USE_JAU_ITER_ALGO 1
55 
56 template<class T>
57 const DataType01 * findDataSet01_hash(T& data, DataType01 const & elem) noexcept {
58  auto search = data.find(elem);
59  if( search != data.end() ) {
60  return &(*search);
61  }
62  return nullptr;
63 }
64 
65 template<class T>
66 static int test_00_list_itr(T& data) {
67  int some_number = 0; // add some validated work, avoiding any 'optimization away'
68  jau::for_each_const(data, [&some_number](const DataType01 & e) {
69  some_number += e.nop();
70  } );
71  REQUIRE(some_number > 0);
72  return some_number;
73 }
74 
75 template<class T, typename Size_type>
76 static void test_00_seq_find_itr(T& data) {
78  const Size_type size = data.size();
79  Size_type fi = 0, i=0;
80 
81  for(; i<size && a0.next(); ++i) {
82  DataType01 elem(a0, static_cast<uint8_t>(1));
83  const DataType01 *found = jau::find_const<T>(data, elem);
84  if( nullptr != found ) {
85  ++fi;
86  found->nop();
87  }
88  }
89  REQUIRE(fi == i);
90 }
91 
92 template<class T>
93 static void test_00_seq_find_hash(T& data) {
95  const std::size_t size = data.size();
96  std::size_t fi = 0, i=0;
97 
98  for(; i<size && a0.next(); ++i) {
99  DataType01 elem(a0, static_cast<uint8_t>(1));
100  const DataType01 *found = findDataSet01_hash(data, elem);
101  if( nullptr != found ) {
102  ++fi;
103  found->nop();
104  }
105  }
106  REQUIRE(fi == i);
107 }
108 
109 template<class T, typename Size_type>
110 static void test_00_seq_fill(T& data, const Size_type size) {
111  Addr48Bit a0(start_addr);
112  Size_type i=0;
113 
114  for(; i<size && a0.next(); ++i) {
115  data.emplace_back( a0, static_cast<uint8_t>(1) );
116  }
117  REQUIRE(i == data.size());
118 }
119 
120 template<class T, typename Size_type>
121 static void test_00_seq_fill_unique_itr(T& data, const Size_type size) {
122  Addr48Bit a0(start_addr);
123  Size_type i=0, fi=0;
124 
125  for(; i<size && a0.next(); ++i) {
126  DataType01 elem(a0, static_cast<uint8_t>(1));
127  const DataType01* exist = jau::find_const<T>(data, elem);
128  if( nullptr == exist ) {
129  data.push_back( std::move( elem ) );
130  ++fi;
131  }
132  }
133  REQUIRE(i == data.size());
134  REQUIRE(fi == size);
135 }
136 
137 template<class T>
138 static void test_00_seq_fill_unique_hash(T& data, const std::size_t size) {
139  Addr48Bit a0(start_addr);
140  std::size_t i=0, fi=0;
141 
142  for(; i<size && a0.next(); ++i) {
143  if( data.emplace(a0, static_cast<uint8_t>(1)).second ) {
144  ++fi;
145  }
146  }
147  REQUIRE(i == data.size());
148  REQUIRE(fi == size);
149 }
150 
151 template<class T>
152 static void print_mem(const std::string& pre, const T& data) {
153  std::size_t bytes_element = sizeof(DataType01);
154  std::size_t elements = data.size();
155  std::size_t bytes_net = elements * bytes_element;
156  std::size_t bytes_total = data.get_allocator().memory_usage;
157  double overhead = 0 == bytes_total ? 0.0 : ( 0 == bytes_net ? 10.0 : (double)bytes_total / (double)bytes_net );
158  printf("Mem: %s: Elements %s x %zu bytes; %s, %lf ratio\n",
159  pre.c_str(), to_decstring(elements, ',', 5).c_str(),
160  bytes_element, data.get_allocator().toString(10, 5).c_str(), overhead);
161  // 5: 1,000
162  // 7: 100,000
163  // 9: 1,000,000
164 }
165 
166 
167 /****************************************************************************************
168  ****************************************************************************************/
169 
170 template<class T, typename Size_type>
171 static bool test_01_seq_fill_list_itr(const std::string& type_id, const Size_type size0, const Size_type reserve0, const bool do_print_mem) {
172  T data;
173  REQUIRE(0 == data.get_allocator().memory_usage);
174  REQUIRE(data.size() == 0);
175  // if( do_print_mem ) { print_mem(type_id+" 01 (empty)", data); }
176 
177  if( 0 < reserve0 ) {
178  data.reserve(reserve0);
179  REQUIRE(data.size() == 0);
180  REQUIRE(0 != data.get_allocator().memory_usage);
181  REQUIRE(data.capacity() == reserve0);
182  }
183 
184  test_00_seq_fill<T, Size_type>(data, size0);
185  REQUIRE(0 != data.get_allocator().memory_usage);
186  REQUIRE(data.size() == size0);
187 
188  test_00_list_itr<T>(data);
189  REQUIRE(0 != data.get_allocator().memory_usage);
190  REQUIRE(data.size() == size0);
191  if( do_print_mem ) { print_mem(type_id+" 01 (full_)", data); }
192 
193  data.clear();
194  REQUIRE(data.size() == 0);
195  // if( do_print_mem ) { print_mem(type_id+" 01 (clear)", data); }
196  // REQUIRE(0 == data.get_allocator().memory_usage);
197  return data.size() == 0;
198 }
199 
200 template<class T>
201 static std::size_t get_capacity(const T& data) {
202  const std::size_t bucket_count = data.bucket_count();
203  std::size_t capacity = 0;
204  for(std::size_t i=0; i<bucket_count; i++) {
205  capacity = data.bucket_size(i);
206  }
207  return capacity;
208 }
209 
210 static bool test_01_seq_fill_list_hash(const std::string& type_id, const std::size_t size0, const std::size_t reserve0, const bool do_print_mem) {
211  typedef std::unordered_set<DataType01, std::hash<DataType01>, std::equal_to<DataType01>, counting_allocator<DataType01>> DataType01Set;
212  DataType01Set data;
213  REQUIRE(0 == data.get_allocator().memory_usage);
214  REQUIRE(data.size() == 0);
215  // if( do_print_mem ) { print_mem(type_id+" 01 (empty)", data); }
216 
217  if( 0 < reserve0 ) {
218  data.reserve(reserve0);
219  REQUIRE(data.size() == 0);
220  REQUIRE(0 != data.get_allocator().memory_usage);
221  REQUIRE(get_capacity<DataType01Set>(data) >= reserve0);
222  }
223 
224  test_00_seq_fill_unique_hash(data, size0);
225  REQUIRE(0 != data.get_allocator().memory_usage);
226  REQUIRE(data.size() == size0);
227 
228  test_00_list_itr<DataType01Set>(data);
229  REQUIRE(0 != data.get_allocator().memory_usage);
230  REQUIRE(data.size() == size0);
231  if( do_print_mem ) { print_mem(type_id+" 01 (full_)", data); }
232 
233  data.clear();
234  REQUIRE(data.size() == 0);
235  // if( do_print_mem ) { print_mem(type_id+" 01 (clear)", data); }
236  // REQUIRE(0 == data.get_allocator().memory_usage);
237  return data.size() == 0;
238 }
239 
240 template<class T, typename Size_type>
241 static bool test_02_seq_fillunique_find_itr(const std::string& type_id, const Size_type size0, const Size_type reserve0) {
242  (void)type_id;
243  T data;
244  REQUIRE(data.size() == 0);
245 
246  if( 0 < reserve0 ) {
247  data.reserve(reserve0);
248  REQUIRE(data.size() == 0);
249  REQUIRE(data.capacity() == reserve0);
250  }
251 
252  test_00_seq_fill_unique_itr<T, Size_type>(data, size0);
253  REQUIRE(data.size() == size0);
254 
255  test_00_seq_find_itr<T, Size_type>(data);
256  REQUIRE(data.size() == size0);
257 
258  data.clear();
259  REQUIRE(data.size() == 0);
260  return data.size() == 0;
261 }
262 
263 static bool test_02_seq_fillunique_find_hash(const std::string& type_id, const std::size_t size0, const std::size_t reserve0) {
264  (void)type_id;
265  typedef std::unordered_set<DataType01, std::hash<DataType01>, std::equal_to<DataType01>, std::allocator<DataType01>> DataType01Set;
266  DataType01Set data;
267  REQUIRE(data.size() == 0);
268 
269  if( 0 < reserve0 ) {
270  data.reserve(reserve0);
271  REQUIRE(data.size() == 0);
272  // REQUIRE(0 != data.get_allocator().memory_usage);
273  // REQUIRE(get_capacity<DataType01Set>(data) >= reserve0);
274  }
275 
276  test_00_seq_fill_unique_hash(data, size0);
277  REQUIRE(data.size() == size0);
278 
279  test_00_seq_find_hash(data);
280  REQUIRE(data.size() == size0);
281 
282  data.clear();
283  REQUIRE(data.size() == 0);
284  return data.size() == 0;
285 }
286 
287 /****************************************************************************************
288  ****************************************************************************************/
289 
290 template<class T, typename Size_type>
291 static bool footprint_fillseq_list_itr(const std::string& type_id, const bool do_rserv) {
292  // test_01_seq_fill_list_itr<T, Size_type>(type_id, 25, do_rserv? 25 : 0, true);
293  test_01_seq_fill_list_itr<T, Size_type>(type_id, 50, do_rserv? 50 : 0, true);
294  if( !catch_auto_run ) {
295  test_01_seq_fill_list_itr<T, Size_type>(type_id, 100, do_rserv? 100 : 0, true);
296  test_01_seq_fill_list_itr<T, Size_type>(type_id, 1000, do_rserv? 1000 : 0, true);
297  }
298  return true;
299 }
300 
301 static bool footprint_fillseq_list_hash(const std::string& type_id, const bool do_rserv) {
302  // test_01_seq_fill_list_hash(type_id, 25, do_rserv? 25 : 0, true);
303  test_01_seq_fill_list_hash(type_id, 50, do_rserv? 50 : 0, true);
304  if( !catch_auto_run ) {
305  test_01_seq_fill_list_hash(type_id, 100, do_rserv? 100 : 0, true);
306  test_01_seq_fill_list_hash(type_id, 1000, do_rserv? 1000 : 0, true);
307  }
308  return true;
309 }
310 
311 template<class T, typename Size_type>
312 static bool benchmark_fillunique_find_itr(const std::string& title_pre, const std::string& type_id,
313  const bool do_rserv) {
314  if( catch_perf_analysis ) {
315  BENCHMARK(title_pre+" FillUni_List 1000") {
316  return test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 1000, do_rserv? 1000 : 0);
317  };
318  // test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 100000, do_rserv? 100000 : 0);
319  return true;
320  }
321  if( catch_auto_run ) {
322  test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 50, do_rserv? 50 : 0);
323  return true;
324  }
325  // BENCHMARK(title_pre+" FillUni_List 25") {
326  // return test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 25, do_rserv? 25 : 0);
327  // };
328  BENCHMARK(title_pre+" FillUni_List 50") {
329  return test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 50, do_rserv? 50 : 0);
330  };
331  BENCHMARK(title_pre+" FillUni_List 100") {
332  return test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 100, do_rserv? 100 : 0);
333  };
334  BENCHMARK(title_pre+" FillUni_List 1000") {
335  return test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 1000, do_rserv? 1000 : 0);
336  };
337  return true;
338 }
339 
340 static bool benchmark_fillunique_find_hash(const std::string& title_pre, const std::string& type_id,
341  const bool do_rserv) {
342  if( catch_perf_analysis ) {
343  BENCHMARK(title_pre+" FillUni_List 1000") {
344  return test_02_seq_fillunique_find_hash(type_id, 1000, do_rserv? 1000 : 0);
345  };
346  // test_02_seq_fillunique_find_hash(type_id, 100000, do_rserv? 100000 : 0, false);
347  return true;
348  }
349  if( catch_auto_run ) {
350  test_02_seq_fillunique_find_hash(type_id, 50, do_rserv? 50 : 0);
351  return true;
352  }
353  // BENCHMARK(title_pre+" FillUni_List 25") {
354  // return test_02_seq_fillunique_find_hash(type_id, 25, do_rserv? 25 : 0);
355  // };
356  BENCHMARK(title_pre+" FillUni_List 50") {
357  return test_02_seq_fillunique_find_hash(type_id, 50, do_rserv? 50 : 0);
358  };
359  BENCHMARK(title_pre+" FillUni_List 100") {
360  return test_02_seq_fillunique_find_hash(type_id, 100, do_rserv? 100 : 0);
361  };
362  BENCHMARK(title_pre+" FillUni_List 1000") {
363  return test_02_seq_fillunique_find_hash(type_id, 1000, do_rserv? 1000 : 0);
364  };
365  return true;
366 }
367 
368 /****************************************************************************************
369  ****************************************************************************************/
370 TEST_CASE( "Memory Footprint 01 - Fill Sequential and List", "[datatype][footprint]" ) {
371  if( catch_perf_analysis ) {
372  footprint_fillseq_list_hash("hash__set_empty_", false);
373  footprint_fillseq_list_itr< jau::cow_vector<DataType01, counting_allocator<DataType01>>, std::size_t>("cowstdvec_empty_", false);
374  footprint_fillseq_list_itr< jau::cow_darray<DataType01, counting_callocator<DataType01>, jau::nsize_t>, jau::nsize_t>("cowdarray_empty_", false);
375  return;
376  }
377  footprint_fillseq_list_hash("hash__set_empty_", false);
378  footprint_fillseq_list_itr< std::vector<DataType01, counting_allocator<DataType01>>, std::size_t>("stdvec_empty_", false);
379  footprint_fillseq_list_itr< jau::darray<DataType01, counting_callocator<DataType01>, jau::nsize_t>, jau::nsize_t>("darray_empty_", false);
380  footprint_fillseq_list_itr< jau::cow_vector<DataType01, counting_allocator<DataType01>>, std::size_t>("cowstdvec_empty_", false);
381  footprint_fillseq_list_itr< jau::cow_darray<DataType01, counting_callocator<DataType01>, jau::nsize_t>, jau::nsize_t>("cowdarray_empty_", false);
382 }
383 
384 TEST_CASE( "Perf Test 02 - Fill Unique and List, empty and reserve", "[datatype][unique]" ) {
385  if( catch_perf_analysis ) {
386  benchmark_fillunique_find_hash("HashSet_NoOrdr_empty", "hash__set_empty_", false);
387  benchmark_fillunique_find_itr< jau::cow_vector<DataType01, std::allocator<DataType01>>, std::size_t>("COW_Vector_empty_itr", "cowstdvec_empty_", false);
388  benchmark_fillunique_find_itr< jau::cow_darray<DataType01, jau::callocator<DataType01>, jau::nsize_t>, jau::nsize_t>("COW_DArray_empty_itr", "cowdarray_empty_", false);
389 
390  return;
391  }
392  benchmark_fillunique_find_hash("HashSet_NoOrdr_empty", "hash__set_empty_", false);
393  benchmark_fillunique_find_itr< std::vector<DataType01, std::allocator<DataType01>>, std::size_t>("STD_Vector_empty_itr", "stdvec_empty_", false);
394  benchmark_fillunique_find_itr< jau::darray<DataType01, jau::callocator<DataType01>, jau::nsize_t>, jau::nsize_t>("JAU_DArray_empty_itr", "darray_empty_", false);
395  benchmark_fillunique_find_itr< jau::cow_vector<DataType01, std::allocator<DataType01>>, std::size_t>("COW_Vector_empty_itr", "cowstdvec_empty_", false);
396  benchmark_fillunique_find_itr< jau::cow_darray<DataType01, jau::callocator<DataType01>, jau::nsize_t>, jau::nsize_t>("COW_DArray_empty_itr", "cowdarray_empty_", false);
397 
398  benchmark_fillunique_find_hash("HashSet_NoOrdr_rserv", "hash__set_empty_", true);
399  benchmark_fillunique_find_itr< std::vector<DataType01, std::allocator<DataType01>>, std::size_t>("STD_Vector_rserv_itr", "stdvec_rserv", true);
400  benchmark_fillunique_find_itr< jau::darray<DataType01, jau::callocator<DataType01>, jau::nsize_t>, jau::nsize_t>("JAU_DArray_rserv_itr", "darray_rserv", true);
401  benchmark_fillunique_find_itr< jau::cow_vector<DataType01, std::allocator<DataType01>>, std::size_t>("COW_Vector_rserv_itr", "cowstdvec_rserv", true);
402  benchmark_fillunique_find_itr< jau::cow_darray<DataType01, jau::callocator<DataType01>, jau::nsize_t>, jau::nsize_t>("COW_DArray_rserv_itr", "cowdarray_rserv", true);
403 
404 }
callocator.hpp
test_00_seq_find_hash
static void test_00_seq_find_hash(T &data)
Definition: test_hashset_perf01.cpp:93
jau::to_decstring
std::string to_decstring(const value_type &v, const char separator=',', const nsize_t width=0) noexcept
Produce a decimal string representation of an integral integer value.
Definition: string_util.hpp:143
catch_auto_run
bool catch_auto_run
Run w/o command-line args, i.e.
Definition: catch2_my_main.cpp:39
darray.hpp
test_01_seq_fill_list_itr
static bool test_01_seq_fill_list_itr(const std::string &type_id, const Size_type size0, const Size_type reserve0, const bool do_print_mem)
Definition: test_hashset_perf01.cpp:171
counting_callocator.hpp
footprint_fillseq_list_hash
static bool footprint_fillseq_list_hash(const std::string &type_id, const bool do_rserv)
Definition: test_hashset_perf01.cpp:301
basic_algos.hpp
test_01_seq_fill_list_hash
static bool test_01_seq_fill_list_hash(const std::string &type_id, const std::size_t size0, const std::size_t reserve0, const bool do_print_mem)
Definition: test_hashset_perf01.cpp:210
test_00_seq_fill_unique_hash
static void test_00_seq_fill_unique_hash(T &data, const std::size_t size)
Definition: test_hashset_perf01.cpp:138
TEST_CASE
TEST_CASE("Memory Footprint 01 - Fill Sequential and List", "[datatype][footprint]")
Definition: test_hashset_perf01.cpp:370
test_00_list_itr
static int test_00_list_itr(T &data)
Definition: test_hashset_perf01.cpp:66
get_capacity
static std::size_t get_capacity(const T &data)
Definition: test_hashset_perf01.cpp:201
jau
Definition: basic_algos.hpp:34
jau::counting_allocator
Performance counter std::allocator specialization.
Definition: counting_allocator.hpp:48
test_00_seq_fill
static void test_00_seq_fill(T &data, const Size_type size)
Definition: test_hashset_perf01.cpp:110
print_mem
static void print_mem(const std::string &pre, const T &data)
Definition: test_hashset_perf01.cpp:152
findDataSet01_hash
const DataType01 * findDataSet01_hash(T &data, DataType01 const &elem) noexcept
Definition: test_hashset_perf01.cpp:57
cow_darray.hpp
test_datatype01.hpp
test_00_seq_find_itr
static void test_00_seq_find_itr(T &data)
Definition: test_hashset_perf01.cpp:76
cow_vector.hpp
test_00_seq_fill_unique_itr
static void test_00_seq_fill_unique_itr(T &data, const Size_type size)
Definition: test_hashset_perf01.cpp:121
start_addr_b
static uint8_t start_addr_b[]
Definition: test_hashset_perf01.cpp:50
test_02_seq_fillunique_find_itr
static bool test_02_seq_fillunique_find_itr(const std::string &type_id, const Size_type size0, const Size_type reserve0)
Definition: test_hashset_perf01.cpp:241
jau::for_each_const
constexpr UnaryFunction for_each_const(T &data, UnaryFunction f, std::enable_if_t< is_cow_type< T >::value, bool >=true) noexcept
Definition: basic_algos.hpp:323
counting_allocator.hpp
start_addr
static Addr48Bit start_addr(start_addr_b)
jau::nsize_t
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:44
test_02_seq_fillunique_find_hash
static bool test_02_seq_fillunique_find_hash(const std::string &type_id, const std::size_t size0, const std::size_t reserve0)
Definition: test_hashset_perf01.cpp:263
DataType01
Definition: test_datatype01.hpp:141
DataType01::nop
int nop() const noexcept
Definition: test_datatype01.hpp:173
basic_types.hpp
Addr48Bit
Definition: test_datatype01.hpp:113
benchmark_fillunique_find_itr
static bool benchmark_fillunique_find_itr(const std::string &title_pre, const std::string &type_id, const bool do_rserv)
Definition: test_hashset_perf01.cpp:312
footprint_fillseq_list_itr
static bool footprint_fillseq_list_itr(const std::string &type_id, const bool do_rserv)
Definition: test_hashset_perf01.cpp:291
catch2_ext.hpp
Addr48Bit::next
bool next() noexcept
Definition: test_datatype01.hpp:113
catch_perf_analysis
bool catch_perf_analysis
Run w/ command-line arg '–perf_analysis'.
Definition: catch2_my_main.cpp:42
benchmark_fillunique_find_hash
static bool benchmark_fillunique_find_hash(const std::string &title_pre, const std::string &type_id, const bool do_rserv)
Definition: test_hashset_perf01.cpp:340