jaulib v1.3.0
Jau Support Library (C++, Java, ..)
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 <cassert>
25#include <cinttypes>
26#include <cstring>
27#include <random>
28#include <vector>
29#include <unordered_set>
30
32
33#include "test_datatype01.hpp"
34
35#include <jau/basic_types.hpp>
36#include <jau/basic_algos.hpp>
38#include <jau/callocator.hpp>
40#include <jau/darray.hpp>
41#include <jau/cow_darray.hpp>
42#include <jau/cow_vector.hpp>
43
44using namespace jau;
45
46static uint8_t start_addr_b[] = {0x20, 0x26, 0x2A, 0x01, 0x20, 0x10};
48
49template<class T>
50const DataType01 * findDataSet01_hash(T& data, DataType01 const & elem) noexcept {
51 auto search = data.find(elem);
52 if( search != data.end() ) {
53 return &(*search);
54 }
55 return nullptr;
56}
57
58template<class T>
59static int test_00_list_itr(T& data) {
60 int some_number = 0; // add some validated work, avoiding any 'optimization away'
61 jau::for_each_const(data, [&some_number](const DataType01 & e) {
62 some_number += e.nop();
63 } );
64 REQUIRE(some_number > 0);
65 return some_number;
66}
67
68template<class T, typename Size_type>
69static void test_00_seq_find_itr(T& data) {
71 const Size_type size = data.size();
72 Size_type fi = 0, i=0;
73
74 for(; i<size && a0.next(); ++i) {
75 DataType01 elem(a0, static_cast<uint8_t>(1));
76 const DataType01 *found = jau::find_const<T>(data, elem);
77 if( nullptr != found ) {
78 ++fi;
79 found->nop();
80 }
81 }
82 REQUIRE(fi == i);
83}
84
85template<class T>
86static void test_00_seq_find_hash(T& data) {
88 const std::size_t size = data.size();
89 std::size_t fi = 0, i=0;
90
91 for(; i<size && a0.next(); ++i) {
92 DataType01 elem(a0, static_cast<uint8_t>(1));
93 const DataType01 *found = findDataSet01_hash(data, elem);
94 if( nullptr != found ) {
95 ++fi;
96 found->nop();
97 }
98 }
99 REQUIRE(fi == i);
100}
101
102template<class T, typename Size_type>
103static void test_00_seq_fill(T& data, const Size_type size) {
105 Size_type i=0;
106
107 for(; i<size && a0.next(); ++i) {
108 data.emplace_back( a0, static_cast<uint8_t>(1) );
109 }
110 REQUIRE(i == data.size());
111}
112
113template<class T, typename Size_type>
114static void test_00_seq_fill_unique_itr(T& data, const Size_type size) {
116 Size_type i=0, fi=0;
117
118 for(; i<size && a0.next(); ++i) {
119 DataType01 elem(a0, static_cast<uint8_t>(1));
120 const DataType01* exist = jau::find_const<T>(data, elem);
121 if( nullptr == exist ) {
122 data.push_back( std::move( elem ) );
123 ++fi;
124 }
125 }
126 REQUIRE(i == data.size());
127 REQUIRE(fi == size);
128}
129
130template<class T>
131static void test_00_seq_fill_unique_hash(T& data, const std::size_t size) {
133 std::size_t i=0, fi=0;
134
135 for(; i<size && a0.next(); ++i) {
136 if( data.emplace(a0, static_cast<uint8_t>(1)).second ) {
137 ++fi;
138 }
139 }
140 REQUIRE(i == data.size());
141 REQUIRE(fi == size);
142}
143
144template<class T>
145static void print_mem(const std::string& pre, const T& data) {
146 std::size_t bytes_element = sizeof(DataType01);
147 std::size_t elements = data.size();
148 std::size_t bytes_net = elements * bytes_element;
149 std::size_t bytes_total = data.get_allocator().memory_usage;
150 double overhead = 0 == bytes_total ? 0.0 : ( 0 == bytes_net ? 10.0 : (double)bytes_total / (double)bytes_net );
151 printf("Mem: %s: Elements %s x %zu bytes; %s, %lf ratio\n",
152 pre.c_str(), to_decstring(elements, ',', 5).c_str(),
153 bytes_element, data.get_allocator().toString(10, 5).c_str(), overhead);
154 // 5: 1,000
155 // 7: 100,000
156 // 9: 1,000,000
157}
158
159
160/****************************************************************************************
161 ****************************************************************************************/
162
163template<class T, typename Size_type>
164static 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) {
165 T data;
166 REQUIRE(0 == data.get_allocator().memory_usage);
167 REQUIRE(data.size() == 0);
168 // if( do_print_mem ) { print_mem(type_id+" 01 (empty)", data); }
169
170 if( 0 < reserve0 ) {
171 data.reserve(reserve0);
172 REQUIRE(data.size() == 0);
173 REQUIRE(0 != data.get_allocator().memory_usage);
174 REQUIRE(data.capacity() == reserve0);
175 }
176
177 test_00_seq_fill<T, Size_type>(data, size0);
178 REQUIRE(0 != data.get_allocator().memory_usage);
179 REQUIRE(data.size() == size0);
180
181 test_00_list_itr<T>(data);
182 REQUIRE(0 != data.get_allocator().memory_usage);
183 REQUIRE(data.size() == size0);
184 if( do_print_mem ) { print_mem(type_id+" 01 (full_)", data); }
185
186 data.clear();
187 REQUIRE(data.size() == 0);
188 // if( do_print_mem ) { print_mem(type_id+" 01 (clear)", data); }
189 // REQUIRE(0 == data.get_allocator().memory_usage);
190 return data.size() == 0;
191}
192
193template<class T>
194static std::size_t get_capacity(const T& data) {
195 const std::size_t bucket_count = data.bucket_count();
196 std::size_t capacity = 0;
197 for(std::size_t i=0; i<bucket_count; i++) {
198 capacity = data.bucket_size(i);
199 }
200 return capacity;
201}
202
203static 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) {
204 typedef std::unordered_set<DataType01, std::hash<DataType01>, std::equal_to<DataType01>, counting_allocator<DataType01>> DataType01Set;
205 DataType01Set data;
206 REQUIRE(0 == data.get_allocator().memory_usage);
207 REQUIRE(data.size() == 0);
208 // if( do_print_mem ) { print_mem(type_id+" 01 (empty)", data); }
209
210 if( 0 < reserve0 ) {
211 data.reserve(reserve0);
212 REQUIRE(data.size() == 0);
213 REQUIRE(0 != data.get_allocator().memory_usage);
214 REQUIRE(get_capacity<DataType01Set>(data) >= reserve0);
215 }
216
217 test_00_seq_fill_unique_hash(data, size0);
218 REQUIRE(0 != data.get_allocator().memory_usage);
219 REQUIRE(data.size() == size0);
220
221 test_00_list_itr<DataType01Set>(data);
222 REQUIRE(0 != data.get_allocator().memory_usage);
223 REQUIRE(data.size() == size0);
224 if( do_print_mem ) { print_mem(type_id+" 01 (full_)", data); }
225
226 data.clear();
227 REQUIRE(data.size() == 0);
228 // if( do_print_mem ) { print_mem(type_id+" 01 (clear)", data); }
229 // REQUIRE(0 == data.get_allocator().memory_usage);
230 return data.size() == 0;
231}
232
233template<class T, typename Size_type>
234static bool test_02_seq_fillunique_find_itr(const std::string& type_id, const Size_type size0, const Size_type reserve0) {
235 (void)type_id;
236 T data;
237 REQUIRE(data.size() == 0);
238
239 if( 0 < reserve0 ) {
240 data.reserve(reserve0);
241 REQUIRE(data.size() == 0);
242 REQUIRE(data.capacity() == reserve0);
243 }
244
245 test_00_seq_fill_unique_itr<T, Size_type>(data, size0);
246 REQUIRE(data.size() == size0);
247
248 test_00_seq_find_itr<T, Size_type>(data);
249 REQUIRE(data.size() == size0);
250
251 data.clear();
252 REQUIRE(data.size() == 0);
253 return data.size() == 0;
254}
255
256static bool test_02_seq_fillunique_find_hash(const std::string& type_id, const std::size_t size0, const std::size_t reserve0) {
257 (void)type_id;
258 typedef std::unordered_set<DataType01, std::hash<DataType01>, std::equal_to<DataType01>, std::allocator<DataType01>> DataType01Set;
259 DataType01Set data;
260 REQUIRE(data.size() == 0);
261
262 if( 0 < reserve0 ) {
263 data.reserve(reserve0);
264 REQUIRE(data.size() == 0);
265 // REQUIRE(0 != data.get_allocator().memory_usage);
266 // REQUIRE(get_capacity<DataType01Set>(data) >= reserve0);
267 }
268
269 test_00_seq_fill_unique_hash(data, size0);
270 REQUIRE(data.size() == size0);
271
273 REQUIRE(data.size() == size0);
274
275 data.clear();
276 REQUIRE(data.size() == 0);
277 return data.size() == 0;
278}
279
280/****************************************************************************************
281 ****************************************************************************************/
282
283template<class T, typename Size_type>
284static bool footprint_fillseq_list_itr(const std::string& type_id, const bool do_rserv) {
285 // test_01_seq_fill_list_itr<T, Size_type>(type_id, 25, do_rserv? 25 : 0, true);
286 test_01_seq_fill_list_itr<T, Size_type>(type_id, 50, do_rserv? 50 : 0, true);
287 if( !catch_auto_run ) {
288 test_01_seq_fill_list_itr<T, Size_type>(type_id, 100, do_rserv? 100 : 0, true);
289 test_01_seq_fill_list_itr<T, Size_type>(type_id, 1000, do_rserv? 1000 : 0, true);
290 }
291 return true;
292}
293
294static bool footprint_fillseq_list_hash(const std::string& type_id, const bool do_rserv) {
295 // test_01_seq_fill_list_hash(type_id, 25, do_rserv? 25 : 0, true);
296 test_01_seq_fill_list_hash(type_id, 50, do_rserv? 50 : 0, true);
297 if( !catch_auto_run ) {
298 test_01_seq_fill_list_hash(type_id, 100, do_rserv? 100 : 0, true);
299 test_01_seq_fill_list_hash(type_id, 1000, do_rserv? 1000 : 0, true);
300 }
301 return true;
302}
303
304template<class T, typename Size_type>
305static bool benchmark_fillunique_find_itr(const std::string& title_pre, const std::string& type_id,
306 const bool do_rserv) {
307 if( catch_perf_analysis ) {
308 BENCHMARK(title_pre+" FillUni_List 1000") {
309 return test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 1000, do_rserv? 1000 : 0);
310 };
311 // test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 100000, do_rserv? 100000 : 0);
312 return true;
313 }
314 if( catch_auto_run ) {
315 test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 50, do_rserv? 50 : 0);
316 return true;
317 }
318 // BENCHMARK(title_pre+" FillUni_List 25") {
319 // return test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 25, do_rserv? 25 : 0);
320 // };
321 BENCHMARK(title_pre+" FillUni_List 50") {
322 return test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 50, do_rserv? 50 : 0);
323 };
324 BENCHMARK(title_pre+" FillUni_List 100") {
325 return test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 100, do_rserv? 100 : 0);
326 };
327 BENCHMARK(title_pre+" FillUni_List 1000") {
328 return test_02_seq_fillunique_find_itr<T, Size_type>(type_id, 1000, do_rserv? 1000 : 0);
329 };
330 return true;
331}
332
333static bool benchmark_fillunique_find_hash(const std::string& title_pre, const std::string& type_id,
334 const bool do_rserv) {
335 if( catch_perf_analysis ) {
336 BENCHMARK(title_pre+" FillUni_List 1000") {
337 return test_02_seq_fillunique_find_hash(type_id, 1000, do_rserv? 1000 : 0);
338 };
339 // test_02_seq_fillunique_find_hash(type_id, 100000, do_rserv? 100000 : 0, false);
340 return true;
341 }
342 if( catch_auto_run ) {
343 test_02_seq_fillunique_find_hash(type_id, 50, do_rserv? 50 : 0);
344 return true;
345 }
346 // BENCHMARK(title_pre+" FillUni_List 25") {
347 // return test_02_seq_fillunique_find_hash(type_id, 25, do_rserv? 25 : 0);
348 // };
349 BENCHMARK(title_pre+" FillUni_List 50") {
350 return test_02_seq_fillunique_find_hash(type_id, 50, do_rserv? 50 : 0);
351 };
352 BENCHMARK(title_pre+" FillUni_List 100") {
353 return test_02_seq_fillunique_find_hash(type_id, 100, do_rserv? 100 : 0);
354 };
355 BENCHMARK(title_pre+" FillUni_List 1000") {
356 return test_02_seq_fillunique_find_hash(type_id, 1000, do_rserv? 1000 : 0);
357 };
358 return true;
359}
360
361/****************************************************************************************
362 ****************************************************************************************/
363TEST_CASE( "Memory Footprint 01 - Fill Sequential and List", "[datatype][footprint]" ) {
364 if( catch_perf_analysis ) {
365 footprint_fillseq_list_hash("hash__set_empty_", false);
366 footprint_fillseq_list_itr< jau::cow_vector<DataType01, counting_allocator<DataType01>>, std::size_t>("cowstdvec_empty_", false);
367 footprint_fillseq_list_itr< jau::cow_darray<DataType01, jau::nsize_t, counting_callocator<DataType01>>, jau::nsize_t>("cowdarray_empty_", false);
368 return;
369 }
370 footprint_fillseq_list_hash("hash__set_empty_", false);
371 footprint_fillseq_list_itr< std::vector<DataType01, counting_allocator<DataType01>>, std::size_t>("stdvec_empty_", false);
372 footprint_fillseq_list_itr< jau::darray<DataType01, jau::nsize_t, counting_callocator<DataType01>>, jau::nsize_t>("darray_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, jau::nsize_t, counting_callocator<DataType01>>, jau::nsize_t>("cowdarray_empty_", false);
375}
376
377TEST_CASE( "Perf Test 02 - Fill Unique and List, empty and reserve", "[datatype][unique]" ) {
378 if( catch_perf_analysis ) {
379 benchmark_fillunique_find_hash("HashSet_NoOrdr_empty", "hash__set_empty_", false);
380 benchmark_fillunique_find_itr< jau::cow_vector<DataType01, std::allocator<DataType01>>, std::size_t>("COW_Vector_empty_itr", "cowstdvec_empty_", false);
381 benchmark_fillunique_find_itr< jau::cow_darray<DataType01, jau::nsize_t, jau::callocator<DataType01>>, jau::nsize_t>("COW_DArray_empty_itr", "cowdarray_empty_", false);
382
383 return;
384 }
385 benchmark_fillunique_find_hash("HashSet_NoOrdr_empty", "hash__set_empty_", false);
386 benchmark_fillunique_find_itr< std::vector<DataType01, std::allocator<DataType01>>, std::size_t>("STD_Vector_empty_itr", "stdvec_empty_", false);
387 benchmark_fillunique_find_itr< jau::darray<DataType01, jau::nsize_t, jau::callocator<DataType01>>, jau::nsize_t>("JAU_DArray_empty_itr", "darray_empty_", false);
388 benchmark_fillunique_find_itr< jau::cow_vector<DataType01, std::allocator<DataType01>>, std::size_t>("COW_Vector_empty_itr", "cowstdvec_empty_", false);
389 benchmark_fillunique_find_itr< jau::cow_darray<DataType01, jau::nsize_t, jau::callocator<DataType01>>, jau::nsize_t>("COW_DArray_empty_itr", "cowdarray_empty_", false);
390
391 benchmark_fillunique_find_hash("HashSet_NoOrdr_rserv", "hash__set_empty_", true);
392 benchmark_fillunique_find_itr< std::vector<DataType01, std::allocator<DataType01>>, std::size_t>("STD_Vector_rserv_itr", "stdvec_rserv", true);
393 benchmark_fillunique_find_itr< jau::darray<DataType01, jau::nsize_t, jau::callocator<DataType01>>, jau::nsize_t>("JAU_DArray_rserv_itr", "darray_rserv", true);
394 benchmark_fillunique_find_itr< jau::cow_vector<DataType01, std::allocator<DataType01>>, std::size_t>("COW_Vector_rserv_itr", "cowstdvec_rserv", true);
395 benchmark_fillunique_find_itr< jau::cow_darray<DataType01, jau::nsize_t, jau::callocator<DataType01>>, jau::nsize_t>("COW_DArray_rserv_itr", "cowdarray_rserv", true);
396
397}
bool catch_perf_analysis
Run w/ command-line arg '–perf_analysis'.
bool catch_auto_run
Run w/o command-line args, i.e.
int nop() const noexcept
constexpr UnaryFunction for_each_const(T &data, UnaryFunction f, std::enable_if_t< is_cow_type< T >::value, bool >=true) noexcept
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
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.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
bool next() noexcept
Performance counter std::allocator specialization.
const DataType01 * findDataSet01_hash(T &data, DataType01 const &elem) noexcept
static bool footprint_fillseq_list_hash(const std::string &type_id, const bool do_rserv)
static bool test_02_seq_fillunique_find_itr(const std::string &type_id, const Size_type size0, const Size_type reserve0)
static void print_mem(const std::string &pre, const T &data)
static bool test_02_seq_fillunique_find_hash(const std::string &type_id, const std::size_t size0, const std::size_t reserve0)
static bool benchmark_fillunique_find_hash(const std::string &title_pre, const std::string &type_id, const bool do_rserv)
static void test_00_seq_fill(T &data, const Size_type size)
static void test_00_seq_find_hash(T &data)
static bool benchmark_fillunique_find_itr(const std::string &title_pre, const std::string &type_id, const bool do_rserv)
static uint8_t start_addr_b[]
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)
static bool footprint_fillseq_list_itr(const std::string &type_id, const bool do_rserv)
static void test_00_seq_fill_unique_hash(T &data, const std::size_t size)
static std::size_t get_capacity(const T &data)
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)
static void test_00_seq_find_itr(T &data)
static Addr48Bit start_addr(start_addr_b)
static void test_00_seq_fill_unique_itr(T &data, const Size_type size)
static int test_00_list_itr(T &data)
TEST_CASE("Memory Footprint 01 - Fill Sequential and List", "[datatype][footprint]")
int printf(const char *format,...)
Operating Systems predefined macros.