jaulib v1.3.0
Jau Support Library (C++, Java, ..)
test_functional_perf.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2022 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 <string>
28
29#if !FUNCTIONAL_PROVIDED
30 #include <jau/functional.hpp>
31 static std::string impl_name = "jau/functional.hpp";
32#endif
33
34#ifndef FUNCTIONAL_IMPL
35 #define FUNCTIONAL_IMPL 1
36#endif
37
39
40// Test examples.
41
42class TestFunction01 {
43 public:
44 const int loops = 1000000;
45
46 /**
47 * Unit test covering most variants of jau::function<R(A...)
48 */
49 void test00_usage() {
50 INFO("Test 00_usage: START: Implementation = functional "+std::to_string( FUNCTIONAL_IMPL ));
51 fprintf(stderr, "Implementation: functional %d, is_rtti_available %d, limited_lambda_id %d\n",
53 {
54 // Test capturing lambdas
55 volatile int i = 100;
56
57 {
58 jau::function<int(int)> fa0 = [&](int a) -> int {
59 return i + a;
60 };
61 fprintf(stderr, "lambda.ref: %s\n", fa0.toString().c_str());
62 REQUIRE( jau::func::target_type::lambda == fa0.type() );
63 }
64
65 {
66 jau::function<int(int)> fa0 = [i](int a) -> int {
67 return i + a;
68 };
69 fprintf(stderr, "lambda.copy: %s\n", fa0.toString().c_str());
70 REQUIRE( jau::func::target_type::lambda == fa0.type() );
71 }
72 }
73 {
74 // Test non-capturing lambdas
75 jau::function<int(int)> f_1 = [](int a) -> int {
76 return a + 100;
77 } ;
78 fprintf(stderr, "lambda.plain: %s\n", f_1.toString().c_str());
79 REQUIRE( jau::func::target_type::lambda == f_1.type() );
80 }
81#if ( FUNCTIONAL_IMPL == 1 )
82 {
83 // Test non-capturing y-lambdas, using auto
84 jau::function<int(int)> f_1 = jau::function<int(int)>::bind_ylambda( [](auto& self, int x) -> int {
85 if( 0 == x ) {
86 return 1;
87 } else {
88 return x * self(x-1);
89 }
90 } );
91 fprintf(stderr, "ylambda.plain1 %s\n", f_1.toString().c_str());
92 REQUIRE( jau::func::target_type::ylambda == f_1.type() );
93 }
94 {
95 // Test non-capturing y-lambdas, using explicit function<int(int)>::delegate_type
96 jau::function<int(int)> f_1 = jau::function<int(int)>::bind_ylambda( [](jau::function<int(int)>::delegate_type& self, int x) -> int {
97 if( 0 == x ) {
98 return 1;
99 } else {
100 return x * self(x-1);
101 }
102 } );
103 fprintf(stderr, "ylambda.plain2 %s\n", f_1.toString().c_str());
104 REQUIRE( jau::func::target_type::ylambda == f_1.type() );
105 }
106#endif
107 {
108 // free, result void and no params
109 typedef void(*cfunc)();
110 jau::function<void()> fl_0 = (cfunc) ( []() -> void {
111 // nop
112 } );
113 fprintf(stderr, "freeA.0 %s\n", fl_0.toString().c_str());
114 REQUIRE( jau::func::target_type::free == fl_0.type() );
115 }
116 {
117 // member, result non-void
118 jau::function<int(int)> f2a_0(this, &TestFunction01::func02a_member);
119 fprintf(stderr, "member: %s\n", f2a_0.toString().c_str());
120 REQUIRE( jau::func::target_type::member == f2a_0.type() );
121 }
122 {
123 // member, result void
124 jau::function<void(int&, int)> f2a_0(this, &TestFunction01::func12a_member);
125 fprintf(stderr, "member: %s\n", f2a_0.toString().c_str());
126 REQUIRE( jau::func::target_type::member == f2a_0.type() );
127 }
128 {
129 // Lambda alike w/ explicit capture by value, result non-void
130 int offset100 = 100;
131
132 typedef int(*cfunc)(int&, int); // to force non-capturing lambda into a free function template type deduction
133
134 jau::function<int(int)> f5_o100_1 = jau::bind_capval(offset100,
135 (cfunc) ( [](int& capture, int i)->int {
136 int res = i+10000+capture;
137 return res;
138 } ) );
139 fprintf(stderr, "capval.small: %s\n", f5_o100_1.toString().c_str());
140 }
141 {
142 // Lambda alike w/ explicit capture by value, result non-void
143 struct blob {
144 int offset100 = 100;
145 uint64_t lala0 = 0;
146 uint64_t lala1 = 1;
147 uint64_t lala2 = 2;
148 uint64_t lala3 = 3;
149
150 bool operator==(const blob& rhs) const noexcept {
151 return offset100 == rhs.offset100 &&
152 lala0 == rhs.lala0 &&
153 lala1 == rhs.lala1 &&
154 lala2 == rhs.lala2 &&
155 lala3 == rhs.lala3;
156 }
157 bool operator!=(const blob& rhs) const noexcept
158 { return !( *this == rhs ); }
159
160 };
161 blob b0;
162
163 typedef int(*cfunc)(blob&, int); // to force non-capturing lambda into a free function template type deduction
164
165 jau::function<int(int)> f5_o100_1 = jau::bind_capval(b0,
166 (cfunc) ( [](blob& capture, int i)->int {
167 int res = i+10000+capture.offset100;
168 return res;
169 } ) );
170 fprintf(stderr, "capval.big: %s\n", f5_o100_1.toString().c_str());
171 }
172 {
173 // Lambda alike w/ explicit capture by reference, result non-void
174 int offset100 = 100;
175
176 typedef int(*cfunc)(int*, int); // to force non-capturing lambda into a free function template type deduction
177
178 jau::function<int(int)> f7_o100_1 = jau::bind_capref<int, int, int>(&offset100,
179 (cfunc) ( [](int* capture, int i)->int {
180 int res = i+10000+(*capture);
181 return res;;
182 } ) );
183 fprintf(stderr, "capref: %s\n", f7_o100_1.toString().c_str());
184 REQUIRE( jau::func::target_type::capref == f7_o100_1.type() );
185 }
186 {
187 // std::function lambda plain
188 std::function<int(int i)> func4a_stdlambda = [](int i)->int {
189 int res = i+100;
190 return res;;
191 };
192 jau::function<int(int)> f = jau::bind_std(100, func4a_stdlambda);
193 fprintf(stderr, "std.lambda pl: %s\n", f.toString().c_str());
194 fprintf(stderr, " (net std.lambda): sizeof %zu\n", sizeof(func4a_stdlambda));
195 REQUIRE( jau::func::target_type::std == f.type() );
196 }
197 {
198 // std::function lambda capture
199 volatile int i = 100;
200 std::function<int(int)> func4a_stdlambda = [&](int a) -> int {
201 return i + a;
202 };
203 jau::function<int(int)> f = jau::bind_std(100, func4a_stdlambda);
204
205 fprintf(stderr, "std.lambda cp: %s\n", f.toString().c_str());
206 fprintf(stderr, " (net std.lambda): sizeof %zu\n", sizeof(func4a_stdlambda));
207 }
208 }
209
210 void test10_perf() {
211 INFO("Test 00_usage: START");
212
213 // free raw func
214 {
215 BENCHMARK("free_rawfunc") {
216 volatile int r=0;
217 for(int i=0; i<loops; ++i) {
218 r = r + TestFunction01::Func03a_static(i);
219 }
220 return r;
221 };
222 }
223
224 // free native function pointer
225 {
226 native_func_t f = TestFunction01::Func03a_static;
227
228 BENCHMARK("free_cfuncptr") {
229 volatile int r=0;
230 for(int i=0; i<loops; ++i) {
231 r = r + f(i);
232 }
233 return r;
234 };
235 }
236
237 // free std::function
238 {
239 std::function<int(int)> f = TestFunction01::Func03a_static;
240
241 BENCHMARK("free_stdfunc") {
242 volatile int r=0;
243 for(int i=0; i<loops; ++i) {
244 r = r + f(i);
245 }
246 return r;
247 };
248 }
249
250 // free, jau::function
251 {
252 jau::function<int(int)> f = jau::bind_free(&TestFunction01::Func03a_static);
253
254 BENCHMARK("free_jaufunc") {
255 volatile int r=0;
256 for(int i=0; i<loops; ++i) {
257 r = r + f(i);
258 }
259 return r;
260 };
261 }
262
263 // member raw function
264 {
265 BENCHMARK("member_rawfunc") {
266 volatile int r=0;
267 for(int i=0; i<loops; ++i) {
268 r = r + func02a_member(i);
269 }
270 return r;
271 };
272 }
273
274 // member std::bind unspecific
275 {
276 using namespace std::placeholders; // for _1, _2, _3...
277 auto f = std::bind(&TestFunction01::func02a_member, this, _1);
278
279 BENCHMARK("member_stdbind_unspec") {
280 volatile int r=0;
281 for(int i=0; i<loops; ++i) {
282 r = r + f(i);
283 }
284 return r;
285 };
286 }
287
288 // member jau::function
289 {
290 jau::function<int(int)> f = jau::bind_member(this, &TestFunction01::func02a_member);
291
292 BENCHMARK("member_jaufunc") {
293 volatile int r=0;
294 for(int i=0; i<loops; ++i) {
295 r = r + f(i);
296 }
297 return r;
298 };
299 }
300
301 // lambda w/ explicit capture by value, jau::function
302 {
303 int offset100 = 100;
304
305 int(*func5a_capture)(int&, int) = [](int& capture, int i)->int {
306 int res = i+capture;
307 return res;
308 };
309 jau::function<int(int)> f = jau::bind_capval(offset100, func5a_capture);
310
311 BENCHMARK("capval_small_jaufunc") {
312 volatile int r=0;
313 for(int i=0; i<loops; ++i) {
314 r = r + f(i);
315 }
316 return r;
317 };
318 }
319
320 {
321 // Lambda alike w/ explicit capture by value, result non-void
322 struct blob {
323 int offset100 = 100;
324 uint64_t lala0 = 0;
325 uint64_t lala1 = 1;
326 uint64_t lala2 = 2;
327 uint64_t lala3 = 3;
328
329 bool operator==(const blob& rhs) const noexcept {
330 return offset100 == rhs.offset100 &&
331 lala0 == rhs.lala0 &&
332 lala1 == rhs.lala1 &&
333 lala2 == rhs.lala2 &&
334 lala3 == rhs.lala3;
335 }
336 bool operator!=(const blob& rhs) const noexcept
337 { return !( *this == rhs ); }
338
339 };
340 blob b0;
341
342 typedef int(*cfunc)(blob&, int); // to force non-capturing lambda into a free function template type deduction
343
344 jau::function<int(int)> f = jau::bind_capval(b0,
345 (cfunc) ( [](blob& capture, int i)->int {
346 int res = i+10000+capture.offset100;
347 return res;
348 } ) );
349
350 BENCHMARK("capval_big_jaufunc") {
351 volatile int r=0;
352 for(int i=0; i<loops; ++i) {
353 r = r + f(i);
354 }
355 return r;
356 };
357 }
358
359 // lambda w/ explicit capture by reference, jau::function
360 {
361 int offset100 = 100;
362
363 int(*func7a_capture)(int*, int) = [](int* capture, int i)->int {
364 int res = i+*capture;
365 return res;
366 };
367 jau::function<int(int)> f = jau::bind_capref(&offset100, func7a_capture);
368
369 BENCHMARK("capref_jaufunc") {
370 volatile int r=0;
371 for(int i=0; i<loops; ++i) {
372 r = r + f(i);
373 }
374 return r;
375 };
376 }
377
378 // plain std::function lambda
379 {
380 std::function<int(int i)> f = [](int i)->int {
381 int res = i+100;
382 return res;;
383 };
384
385 BENCHMARK("lambda_plain_std_function") {
386 volatile int r=0;
387 for(int i=0; i<loops; ++i) {
388 r = r + f(i);
389 }
390 return r;
391 };
392 }
393
394 // plain jau::function lambda
395 {
396 jau::function<int(int)> f = [](int a) -> int {
397 return 100+ a;
398 };
399
400 BENCHMARK("lambda_plain_jaufunc") {
401 volatile int r=0;
402 for(int i=0; i<loops; ++i) {
403 r = r + f(i);
404 }
405 return r;
406 };
407 }
408
409 // capture std::function lambda
410 {
411 volatile int captured = 100;
412
413 std::function<int(int)> f = [&](int a) -> int {
414 return captured + a;
415 };
416
417 BENCHMARK("lambda_capt_std_function") {
418 volatile int r=0;
419 for(int i=0; i<loops; ++i) {
420 r = r + f(i);
421 }
422 return r;
423 };
424 }
425
426 // capture jau::function lambda
427 {
428 volatile int captured = 100;
429
430 jau::function<int(int)> f = [&](int a) -> int {
431 return captured + a;
432 };
433
434 BENCHMARK("lambda_capt_jaufunc") {
435 volatile int r=0;
436 for(int i=0; i<loops; ++i) {
437 r = r + f(i);
438 }
439 return r;
440 };
441 }
442#if ( FUNCTIONAL_IMPL == 1 )
443 {
444 jau::function<int(int)> f = jau::function<int(int)>::bind_ylambda( [](auto& self, int x) -> int {
445 (void)self; // no-use
446 return 100+x;
447 } );
448
449 BENCHMARK("ylambda_none_jaufunc") {
450 volatile int r=0;
451 for(int i=0; i<loops; ++i) {
452 r = r + f(i);
453 }
454 return r;
455 };
456
457 }
458#endif
459
460 REQUIRE( true == true );
461
462 INFO("Test 00_usage: END");
463 }
464
465 private:
466
467 typedef int(*native_func_t)(int);
468 typedef std::function<int(int)> std_func_t;
469 typedef jau::function<int(int)> jau_func_t;
470
471 // template<typename R, typename... A>
472 typedef int(*MyCFunc0)(int);
473
474 typedef jau::function<int(int)> MyClassFunction0;
475 typedef jau::function<void(int&, int)> MyClassFunction1;
476 typedef jau::function<void()> MyClassFunction2;
477
478 int func02a_member(int i) {
479 int res = i+100;
480 return res;;
481 }
482 int func02b_member(int i) noexcept {
483 int res = i+1000;
484 return res;
485 }
486 static int Func03a_static(int i) {
487 int res = i+100;
488 return res;
489 }
490 static int Func03b_static(int i) noexcept {
491 int res = i+1000;
492 return res;
493 }
494
495 void func12a_member(int& r, const int i) {
496 r = i+100;
497 }
498 void func12b_member(int& r, const int i) noexcept {
499 r = i+1000;
500 }
501 static void Func13a_static(int& r, const int i) {
502 r = i+100;
503 }
504 static void Func13b_static(int& r, const int i) noexcept {
505 r = i+1000;
506 }
507
508 void func20a_member() {
509 // nop
510 }
511 static void Func20a_static() {
512 // nop
513 }
514
515 static jau::function<int(int)> lambda_01() {
516 static int i = 100;
517 jau::function<int(int)> f = [&](int a) -> int {
518 return i + a;
519 };
520 return f;
521 }
522 static jau::function<int(int)> lambda_02() {
523 int i = 100;
524 jau::function<int(int)> f = [i](int a) -> int {
525 return i + a;
526 };
527 return f;
528 }
529
530};
531
void test00_usage()
Unit test covering most variants of jau::function<R(A...)
Class template jau::function is a general-purpose static-polymorphic function wrapper.
static constexpr const bool limited_lambda_id
Static constexpr boolean indicating whether resulting type_info uniqueness is limited for lambda func...
bool operator!=(const alphabet &lhs, const alphabet &rhs) noexcept
Definition: base_codec.hpp:99
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
bool operator==(const alphabet &lhs, const alphabet &rhs) noexcept
Definition: base_codec.hpp:103
consteval_cxx20 bool is_rtti_available() noexcept
Returns true if compiled with RTTI available.
jau::function< R(A...)> bind_member(C1 *base, R(C0::*mfunc)(A...)) noexcept
Bind given class instance and non-void member function to an anonymous function using func_member_tar...
jau::function< R(A...)> bind_free(R(*func)(A...)) noexcept
Bind given non-void free-function to an anonymous function using func::free_target_t.
jau::function< R(A...)> bind_std(uint64_t id, std::function< R(A...)> func) noexcept
Bind given non-void std::function to an anonymous function using func::std_target_t.
jau::function< R(A...)> bind_capval(const I &data, R(*func)(I &, A...)) noexcept
Bind given data by copying the captured value and the given non-void function to an anonymous functio...
jau::function< R(A...)> bind_capref(I *data_ptr, R(*func)(I *, A...)) noexcept
Bind given data by passing the captured reference (pointer) to the value and non-void function to an ...
@ lambda
Denotes a func::lambda_target_t.
@ capref
Denotes a func::capref_target_t.
@ member
Denotes a func::member_target_t.
@ free
Denotes a func::free_target_t.
@ std
Denotes a func::std_target_t.
@ ylambda
Denotes a func::ylambda_target_t.
#define FUNCTIONAL_IMPL
METHOD_AS_TEST_CASE(TestFunction01::test00_usage, "00_usage")
static std::string impl_name