Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah_parallel_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
37#include <gtest/gtest.h>
38#include <ah-parallel.H>
39#include <vector>
40#include <list>
41#include <deque>
42#include <array>
43#include <string>
44#include <numeric>
45#include <atomic>
46#include <chrono>
47#include <random>
48#include <algorithm>
49#include <cmath>
50
51using namespace Aleph;
52
53// =============================================================================
54// Test Fixture
55// =============================================================================
56
57class ParallelTest : public ::testing::Test
58{
59protected:
61
62 std::vector<int> empty_vec;
63 std::vector<int> single_vec = {42};
64 std::vector<int> small_vec = {1, 2, 3, 4, 5};
65 std::vector<int> large_vec;
66
67 void SetUp() override
68 {
69 large_vec.resize(10000);
70 std::iota(large_vec.begin(), large_vec.end(), 1);
71 }
72};
73
74// =============================================================================
75// pmaps Tests
76// =============================================================================
77
79{
80 auto result = pmaps(pool, small_vec, [](int x) { return x * x; });
81 ASSERT_EQ(result.size(), 5u);
82 EXPECT_EQ(result, (std::vector<int>{1, 4, 9, 16, 25}));
83}
84
86{
87 auto result = pmaps<double>(pool, small_vec, [](int x) { return x * 1.5; });
88 ASSERT_EQ(result.size(), 5u);
89 EXPECT_NEAR(result[0], 1.5, 0.001);
90 EXPECT_NEAR(result[4], 7.5, 0.001);
91}
92
94{
95 auto result = pmaps(pool, small_vec, [](int x) { return std::to_string(x); });
96 ASSERT_EQ(result.size(), 5u);
97 EXPECT_EQ(result[0], "1");
98 EXPECT_EQ(result[4], "5");
99}
100
102{
103 auto result = pmaps(pool, empty_vec, [](int x) { return x * 2; });
104 EXPECT_TRUE(result.empty());
105}
106
108{
109 auto result = pmaps(pool, single_vec, [](int x) { return x * 2; });
110 ASSERT_EQ(result.size(), 1u);
111 EXPECT_EQ(result[0], 84);
112}
113
115{
116 auto result = pmaps(pool, large_vec, [](int x) { return x * 2; });
117 ASSERT_EQ(result.size(), 10000u);
118 for (size_t i = 0; i < result.size(); ++i)
119 EXPECT_EQ(result[i], static_cast<int>((i + 1) * 2));
120}
121
123{
124 std::list<int> lst = {1, 2, 3, 4, 5};
125 auto result = pmaps(pool, lst, [](int x) { return x * x; });
126 EXPECT_EQ(result, (std::vector<int>{1, 4, 9, 16, 25}));
127}
128
130{
131 auto result = pmaps(pool, large_vec, [](int x) { return x; });
132 for (size_t i = 0; i < result.size(); ++i)
133 EXPECT_EQ(result[i], static_cast<int>(i + 1));
134}
135
136// =============================================================================
137// pfilter Tests
138// =============================================================================
139
141{
142 auto result = pfilter(pool, small_vec, [](int x) { return x % 2 == 0; });
143 EXPECT_EQ(result, (std::vector<int>{2, 4}));
144}
145
147{
148 auto result = pfilter(pool, small_vec, [](int x) { return x % 2 != 0; });
149 EXPECT_EQ(result, (std::vector<int>{1, 3, 5}));
150}
151
153{
154 auto result = pfilter(pool, small_vec, [](int x) { return x > 100; });
155 EXPECT_TRUE(result.empty());
156}
157
159{
160 auto result = pfilter(pool, small_vec, [](int x) { return x > 0; });
161 EXPECT_EQ(result.size(), 5u);
162}
163
165{
166 auto result = pfilter(pool, empty_vec, [](int) { return true; });
167 EXPECT_TRUE(result.empty());
168}
169
171{
172 std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
173 auto result = pfilter(pool, nums, [](int x) { return x % 3 == 0; });
174 EXPECT_EQ(result, (std::vector<int>{3, 6, 9}));
175}
176
178{
179 auto result = pfilter(pool, large_vec, [](int x) { return x % 100 == 0; });
180 EXPECT_EQ(result.size(), 100u);
181 EXPECT_EQ(result[0], 100);
182 EXPECT_EQ(result[99], 10000);
183}
184
185// =============================================================================
186// pfoldl Tests
187// =============================================================================
188
190{
191 int sum = pfoldl(pool, small_vec, 0, std::plus<int>());
192 EXPECT_EQ(sum, 15);
193}
194
196{
197 int product = pfoldl(pool, small_vec, 1, std::multiplies<int>());
198 EXPECT_EQ(product, 120);
199}
200
202{
203 int max_val = pfoldl(pool, small_vec, INT_MIN,
204 [](int a, int b) { return std::max(a, b); });
205 EXPECT_EQ(max_val, 5);
206}
207
209{
210 int result = pfoldl(pool, empty_vec, 42, std::plus<int>());
211 EXPECT_EQ(result, 42);
212}
213
215{
216 long long sum = pfoldl(pool, large_vec, 0LL,
217 [](long long a, int b) { return a + b; });
218 // Sum 1..10000 = 10000*10001/2 = 50005000
219 EXPECT_EQ(sum, 50005000LL);
220}
221
222// =============================================================================
223// pfor_each Tests
224// =============================================================================
225
227{
228 std::vector<int> data = {1, 2, 3, 4, 5};
229 pfor_each(pool, data, [](int& x) { x *= 2; });
230 EXPECT_EQ(data, (std::vector<int>{2, 4, 6, 8, 10}));
231}
232
234{
235 std::atomic<int> count{0};
236 pfor_each(pool, large_vec, [&count](const int& x) {
237 if (x % 2 == 0) ++count;
238 });
239 EXPECT_EQ(count.load(), 5000);
240}
241
243{
244 std::atomic<int> count{0};
245 pfor_each(pool, empty_vec, [&count](int) { ++count; });
246 EXPECT_EQ(count.load(), 0);
247}
248
249// =============================================================================
250// pall, pexists, pnone Tests
251// =============================================================================
252
254{
255 bool result = pall(pool, small_vec, [](int x) { return x > 0; });
256 EXPECT_TRUE(result);
257}
258
260{
261 bool result = pall(pool, small_vec, [](int x) { return x > 3; });
262 EXPECT_FALSE(result);
263}
264
266{
267 bool result = pall(pool, empty_vec, [](int) { return false; });
268 EXPECT_TRUE(result); // Vacuous truth
269}
270
272{
273 bool result = pexists(pool, small_vec, [](int x) { return x == 3; });
274 EXPECT_TRUE(result);
275}
276
278{
279 bool result = pexists(pool, small_vec, [](int x) { return x > 100; });
280 EXPECT_FALSE(result);
281}
282
284{
285 bool result = pexists(pool, empty_vec, [](int) { return true; });
286 EXPECT_FALSE(result);
287}
288
290{
291 bool result = pnone(pool, small_vec, [](int x) { return x < 0; });
292 EXPECT_TRUE(result);
293}
294
296{
297 bool result = pnone(pool, small_vec, [](int x) { return x == 3; });
298 EXPECT_FALSE(result);
299}
300
301// =============================================================================
302// pcount_if Tests
303// =============================================================================
304
306{
307 size_t count = pcount_if(pool, small_vec, [](int x) { return x % 2 == 0; });
308 EXPECT_EQ(count, 2u);
309}
310
312{
313 size_t count = pcount_if(pool, small_vec, [](int x) { return x > 0; });
314 EXPECT_EQ(count, 5u);
315}
316
318{
319 size_t count = pcount_if(pool, small_vec, [](int x) { return x > 100; });
320 EXPECT_EQ(count, 0u);
321}
322
324{
325 size_t count = pcount_if(pool, large_vec, [](int x) { return x % 7 == 0; });
326 EXPECT_EQ(count, 1428u); // floor(10000/7) = 1428
327}
328
329// =============================================================================
330// pfind Tests
331// =============================================================================
332
334{
335 auto idx = pfind(pool, small_vec, [](int x) { return x == 3; });
336 ASSERT_TRUE(idx.has_value());
337 EXPECT_EQ(*idx, 2u);
338}
339
341{
342 auto idx = pfind(pool, small_vec, [](int x) { return x == 100; });
343 EXPECT_FALSE(idx.has_value());
344}
345
347{
348 std::vector<int> data = {1, 2, 3, 3, 3, 4, 5};
349 auto idx = pfind(pool, data, [](int x) { return x == 3; });
350 ASSERT_TRUE(idx.has_value());
351 EXPECT_EQ(*idx, 2u); // First occurrence
352}
353
355{
356 auto idx = pfind(pool, empty_vec, [](int) { return true; });
357 EXPECT_FALSE(idx.has_value());
358}
359
361{
362 std::vector<std::string> words = {"apple", "banana", "cherry"};
363 auto found = pfind_value(pool, words,
364 [](const std::string& s) { return s.length() > 5; });
365 ASSERT_TRUE(found.has_value());
366 EXPECT_EQ(*found, "banana");
367}
368
369// =============================================================================
370// psum, pproduct Tests
371// =============================================================================
372
374{
375 auto sum = psum(pool, small_vec);
376 EXPECT_EQ(sum, 15);
377}
378
380{
381 auto sum = psum(pool, small_vec, 100);
382 EXPECT_EQ(sum, 115);
383}
384
386{
387 auto sum = psum(pool, empty_vec);
388 EXPECT_EQ(sum, 0);
389}
390
392{
393 auto sum = psum<std::vector<int>, long long>(pool, large_vec, 0LL);
394 EXPECT_EQ(sum, 50005000LL);
395}
396
398{
399 auto product = pproduct(pool, small_vec);
400 EXPECT_EQ(product, 120);
401}
402
404{
405 auto product = pproduct(pool, empty_vec);
406 EXPECT_EQ(product, 1); // Identity for multiplication
407}
408
409// =============================================================================
410// pmin, pmax, pminmax Tests
411// =============================================================================
412
414{
415 auto min_val = pmin(pool, small_vec);
416 ASSERT_TRUE(min_val.has_value());
417 EXPECT_EQ(*min_val, 1);
418}
419
421{
422 auto max_val = pmax(pool, small_vec);
423 ASSERT_TRUE(max_val.has_value());
424 EXPECT_EQ(*max_val, 5);
425}
426
428{
429 auto min_val = pmin(pool, empty_vec);
430 EXPECT_FALSE(min_val.has_value());
431}
432
434{
435 auto max_val = pmax(pool, empty_vec);
436 EXPECT_FALSE(max_val.has_value());
437}
438
440{
441 auto result = pminmax(pool, small_vec);
442 ASSERT_TRUE(result.has_value());
443 EXPECT_EQ(result->first, 1);
444 EXPECT_EQ(result->second, 5);
445}
446
448{
449 std::vector<int> data = {5, 1, 3, 2, 4};
450 auto result = pminmax(pool, data);
451 ASSERT_TRUE(result.has_value());
452 EXPECT_EQ(result->first, 1);
453 EXPECT_EQ(result->second, 5);
454}
455
457{
458 auto result = pminmax(pool, large_vec);
459 ASSERT_TRUE(result.has_value());
460 EXPECT_EQ(result->first, 1);
461 EXPECT_EQ(result->second, 10000);
462}
463
464// =============================================================================
465// psort Tests
466// =============================================================================
467
469{
470 std::vector<int> data = {5, 2, 8, 1, 9, 3, 7, 4, 6};
471 psort(pool, data);
472 EXPECT_EQ(data, (std::vector<int>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
473}
474
476{
477 std::vector<int> data = {5, 2, 8, 1, 9, 3, 7, 4, 6};
478 psort(pool, data, std::greater<int>());
479 EXPECT_EQ(data, (std::vector<int>{9, 8, 7, 6, 5, 4, 3, 2, 1}));
480}
481
483{
484 std::vector<int> data;
485 psort(pool, data);
486 EXPECT_TRUE(data.empty());
487}
488
490{
491 std::vector<int> data = {42};
492 psort(pool, data);
493 EXPECT_EQ(data[0], 42);
494}
495
497{
498 std::vector<int> data = {1, 2, 3, 4, 5};
499 psort(pool, data);
500 EXPECT_EQ(data, (std::vector<int>{1, 2, 3, 4, 5}));
501}
502
504{
505 std::vector<int> data = large_vec;
506 std::mt19937 rng(42); // Fixed seed for reproducibility
507 std::shuffle(data.begin(), data.end(), rng);
508 psort(pool, data);
509 for (size_t i = 0; i < data.size(); ++i)
510 EXPECT_EQ(data[i], static_cast<int>(i + 1));
511}
512
513// =============================================================================
514// pzip_for_each Tests
515// =============================================================================
516
518{
519 std::vector<int> a = {1, 2, 3};
520 std::vector<int> b = {4, 5, 6};
521 std::atomic<int> sum{0};
522
523 pzip_for_each(pool, a, b, [&sum](int x, int y) { sum += x * y; });
524 EXPECT_EQ(sum.load(), 32); // 1*4 + 2*5 + 3*6
525}
526
528{
529 std::vector<int> a = {1, 2, 3, 4, 5};
530 std::vector<int> b = {10, 20, 30};
531 std::atomic<int> sum{0};
532
533 pzip_for_each(pool, a, b, [&sum](int x, int y) { sum += x + y; });
534 EXPECT_EQ(sum.load(), 66); // (1+10) + (2+20) + (3+30)
535}
536
538{
539 std::vector<int> a;
540 std::vector<int> b = {1, 2, 3};
541 std::atomic<int> count{0};
542
543 pzip_for_each(pool, a, b, [&count](int, int) { ++count; });
544 EXPECT_EQ(count.load(), 0);
545}
546
547// =============================================================================
548// pzip_maps Tests
549// =============================================================================
550
552{
553 std::vector<int> a = {1, 2, 3};
554 std::vector<int> b = {4, 5, 6};
555
556 auto result = pzip_maps(pool, a, b, [](int x, int y) { return x * y; });
557 EXPECT_EQ(result, (std::vector<int>{4, 10, 18}));
558}
559
561{
562 std::vector<int> a = {1, 2, 3};
563 std::vector<double> b = {1.5, 2.5, 3.5};
564
565 auto result = pzip_maps(pool, a, b, [](int x, double y) { return x + y; });
566 EXPECT_NEAR(result[0], 2.5, 0.001);
567 EXPECT_NEAR(result[1], 4.5, 0.001);
568 EXPECT_NEAR(result[2], 6.5, 0.001);
569}
570
571// =============================================================================
572// ppartition Tests
573// =============================================================================
574
576{
577 auto [evens, odds] = ppartition(pool, small_vec, [](int x) { return x % 2 == 0; });
578 EXPECT_EQ(evens, (std::vector<int>{2, 4}));
579 EXPECT_EQ(odds, (std::vector<int>{1, 3, 5}));
580}
581
583{
584 auto [yes, no] = ppartition(pool, small_vec, [](int x) { return x > 0; });
585 EXPECT_EQ(yes.size(), 5u);
587}
588
590{
591 auto [yes, no] = ppartition(pool, small_vec, [](int x) { return x < 0; });
593 EXPECT_EQ(no.size(), 5u);
594}
595
597{
598 auto [yes, no] = ppartition(pool, empty_vec, [](int) { return true; });
601}
602
604{
605 std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
606 auto [evens, odds] = ppartition(pool, data, [](int x) { return x % 2 == 0; });
607 EXPECT_EQ(evens, (std::vector<int>{2, 4, 6, 8, 10}));
608 EXPECT_EQ(odds, (std::vector<int>{1, 3, 5, 7, 9}));
609}
610
611// =============================================================================
612// Correctness: Sequential vs Parallel Comparison
613// =============================================================================
614
616{
617 // Sequential
618 std::vector<int> seq_result;
619 for (int x : large_vec)
620 seq_result.push_back(x * 2 + 1);
621
622 // Parallel
623 auto par_result = pmaps(pool, large_vec, [](int x) { return x * 2 + 1; });
624
626}
627
629{
630 // Sequential
631 std::vector<int> seq_result;
632 for (int x : large_vec)
633 if (x % 17 == 0)
634 seq_result.push_back(x);
635
636 // Parallel
637 auto par_result = pfilter(pool, large_vec, [](int x) { return x % 17 == 0; });
638
640}
641
643{
644 // Sequential
645 long long seq_sum = 0;
646 for (int x : large_vec)
647 seq_sum += x;
648
649 // Parallel
650 long long par_sum = pfoldl(pool, large_vec, 0LL,
651 [](long long a, int b) { return a + b; });
652
654}
655
656// =============================================================================
657// Performance Benchmarks
658// =============================================================================
659
661{
662 std::vector<int> data(1000000);
663 std::iota(data.begin(), data.end(), 0);
664
665 // Sequential
666 auto seq_start = std::chrono::high_resolution_clock::now();
667 std::vector<int> seq_result;
668 seq_result.reserve(data.size());
669 for (int x : data)
670 seq_result.push_back(x * x + 2 * x + 1);
671 auto seq_end = std::chrono::high_resolution_clock::now();
672 auto seq_duration = std::chrono::duration_cast<std::chrono::microseconds>(
673 seq_end - seq_start).count();
674
675 // Parallel
676 auto par_start = std::chrono::high_resolution_clock::now();
677 auto par_result = pmaps(pool, data, [](int x) { return x * x + 2 * x + 1; });
678 auto par_end = std::chrono::high_resolution_clock::now();
679 auto par_duration = std::chrono::duration_cast<std::chrono::microseconds>(
680 par_end - par_start).count();
681
683
684 double speedup = static_cast<double>(seq_duration) / par_duration;
685 std::cout << "\n=== Benchmark: pmaps ===\n";
686 std::cout << "Data size: 1M elements\n";
687 std::cout << "Sequential: " << seq_duration << " μs\n";
688 std::cout << "Parallel: " << par_duration << " μs\n";
689 std::cout << "Speedup: " << speedup << "x\n";
690 std::cout << "========================\n\n";
691
692 // With multiple threads, parallel overhead may exceed benefits for trivial ops
693 // On CI machines with limited cores or high load, speedup can be < 1.0
694 // Use very relaxed threshold - this test validates correctness, not performance
695 // Performance is validated by filter/sort benchmarks which have higher per-element cost
696 if (pool.num_threads() > 1)
697 EXPECT_GT(speedup, 0.3); // Very relaxed: just ensure parallelization isn't broken
698}
699
701{
702 std::vector<int> data(1000000);
703 std::iota(data.begin(), data.end(), 0);
704
705 auto pred = [](int x) { return x % 7 == 0 || x % 11 == 0; };
706
707 // Sequential
708 auto seq_start = std::chrono::high_resolution_clock::now();
709 std::vector<int> seq_result;
710 for (int x : data)
711 if (pred(x))
712 seq_result.push_back(x);
713 auto seq_end = std::chrono::high_resolution_clock::now();
714 auto seq_duration = std::chrono::duration_cast<std::chrono::microseconds>(
715 seq_end - seq_start).count();
716
717 // Parallel
718 auto par_start = std::chrono::high_resolution_clock::now();
719 auto par_result = pfilter(pool, data, pred);
720 auto par_end = std::chrono::high_resolution_clock::now();
721 auto par_duration = std::chrono::duration_cast<std::chrono::microseconds>(
722 par_end - par_start).count();
723
725
726 double speedup = static_cast<double>(seq_duration) / par_duration;
727 std::cout << "\n=== Benchmark: pfilter ===\n";
728 std::cout << "Data size: 1M elements\n";
729 std::cout << "Sequential: " << seq_duration << " μs\n";
730 std::cout << "Parallel: " << par_duration << " μs\n";
731 std::cout << "Speedup: " << speedup << "x\n";
732 std::cout << "==========================\n\n";
733}
734
736{
737 std::vector<int> data(100000);
738 std::iota(data.begin(), data.end(), 0);
739 std::mt19937 rng(123); // Fixed seed for reproducibility
740 std::shuffle(data.begin(), data.end(), rng);
741
742 std::vector<int> data_copy = data;
743
744 // Sequential
745 auto seq_start = std::chrono::high_resolution_clock::now();
746 std::sort(data.begin(), data.end());
747 auto seq_end = std::chrono::high_resolution_clock::now();
748 auto seq_duration = std::chrono::duration_cast<std::chrono::microseconds>(
749 seq_end - seq_start).count();
750
751 // Parallel
752 auto par_start = std::chrono::high_resolution_clock::now();
753 psort(pool, data_copy);
754 auto par_end = std::chrono::high_resolution_clock::now();
755 auto par_duration = std::chrono::duration_cast<std::chrono::microseconds>(
756 par_end - par_start).count();
757
758 EXPECT_EQ(data, data_copy);
759
760 double speedup = static_cast<double>(seq_duration) / par_duration;
761 std::cout << "\n=== Benchmark: psort ===\n";
762 std::cout << "Data size: 100K elements\n";
763 std::cout << "std::sort: " << seq_duration << " μs\n";
764 std::cout << "psort: " << par_duration << " μs\n";
765 std::cout << "Speedup: " << speedup << "x\n";
766 std::cout << "========================\n\n";
767}
768
769// =============================================================================
770// Thread Safety Tests
771// =============================================================================
772
774{
775 // Run multiple parallel operations simultaneously
776 auto future1 = std::async(std::launch::async, [this]() {
777 return pmaps(pool, large_vec, [](int x) { return x * 2; });
778 });
779
780 auto future2 = std::async(std::launch::async, [this]() {
781 return pfilter(pool, large_vec, [](int x) { return x % 2 == 0; });
782 });
783
784 auto future3 = std::async(std::launch::async, [this]() {
785 return psum<std::vector<int>, long long>(pool, large_vec, 0LL);
786 });
787
788 auto result1 = future1.get();
789 auto result2 = future2.get();
790 auto result3 = future3.get();
791
792 EXPECT_EQ(result1.size(), 10000u);
793 EXPECT_EQ(result2.size(), 5000u);
794 EXPECT_EQ(result3, 50005000LL);
795}
796
797// =============================================================================
798// Edge Cases
799// =============================================================================
800
802{
804
805 auto result = pmaps(single_pool, small_vec, [](int x) { return x * x; });
806 EXPECT_EQ(result, (std::vector<int>{1, 4, 9, 16, 25}));
807}
808
810{
812
813 auto result = pmaps(many_pool, small_vec, [](int x) { return x * x; });
814 EXPECT_EQ(result, (std::vector<int>{1, 4, 9, 16, 25}));
815}
816
818{
819 std::vector<int> huge(100000);
820 std::iota(huge.begin(), huge.end(), 0);
821
822 auto sum = psum<std::vector<int>, long long>(pool, huge, 0LL);
823 // Sum 0..99999 = 99999*100000/2 = 4999950000
824 EXPECT_EQ(sum, 4999950000LL);
825}
826
827// =============================================================================
828// Variadic Zip Tests (N containers)
829// =============================================================================
830
832{
833 std::vector<int> a = {1, 2, 3};
834 std::vector<int> b = {4, 5, 6};
835 std::vector<int> c = {7, 8, 9};
836 std::atomic<int> sum{0};
837
838 pzip_for_each_n(pool, [&sum](int x, int y, int z) {
839 sum += x + y + z;
840 }, a, b, c);
841
842 EXPECT_EQ(sum.load(), 45); // (1+4+7) + (2+5+8) + (3+6+9)
843}
844
846{
847 std::vector<int> a = {1, 2};
848 std::vector<int> b = {3, 4};
849 std::vector<int> c = {5, 6};
850 std::vector<int> d = {7, 8};
851 std::atomic<int> sum{0};
852
853 pzip_for_each_n(pool, [&sum](int w, int x, int y, int z) {
854 sum += w * x * y * z;
855 }, a, b, c, d);
856
857 EXPECT_EQ(sum.load(), 1*3*5*7 + 2*4*6*8); // 105 + 384 = 489
858}
859
861{
862 std::vector<int> a = {1, 2, 3, 4, 5};
863 std::vector<int> b = {10, 20, 30};
864 std::vector<int> c = {100, 200, 300, 400};
865 std::atomic<int> count{0};
866
867 pzip_for_each_n(pool, [&count](int, int, int) {
868 ++count;
869 }, a, b, c);
870
871 EXPECT_EQ(count.load(), 3); // Min length
872}
873
875{
876 std::vector<int> a = {1, 2, 3};
877 std::vector<int> b = {4, 5, 6};
878 std::vector<int> c = {7, 8, 9};
879
880 auto result = pzip_maps_n(pool, [](int x, int y, int z) {
881 return x + y + z;
882 }, a, b, c);
883
884 EXPECT_EQ(result, (std::vector<int>{12, 15, 18}));
885}
886
888{
889 std::vector<int> a;
890 std::vector<int> b = {1, 2, 3};
891
892 auto result = pzip_maps_n(pool, [](int x, int y) {
893 return x + y;
894 }, a, b);
895
896 EXPECT_TRUE(result.empty());
897}
898
900{
901 std::vector<int> a = {1, 2, 3};
902 std::vector<int> b = {4, 5, 6};
903
904 // Dot product using variadic version with combiner
905 int dot = pzip_foldl_n(pool, 0,
906 [](int acc, int x, int y) { return acc + x * y; },
907 std::plus<int>(),
908 a, b);
909
910 EXPECT_EQ(dot, 32); // 1*4 + 2*5 + 3*6
911}
912
914{
915 std::vector<int> a = {1, 2, 3};
916 std::vector<int> b = {2, 3, 4};
917 std::vector<int> c = {3, 4, 5};
918
919 bool result = pzip_all_n(pool, [](int x, int y, int z) {
920 return x < y && y < z;
921 }, a, b, c);
922
923 EXPECT_TRUE(result);
924}
925
927{
928 std::vector<int> a = {1, 2, 3};
929 std::vector<int> b = {2, 3, 4};
930 std::vector<int> c = {3, 2, 5}; // 2 < 3 is true, but 3 < 2 is false
931
932 bool result = pzip_all_n(pool, [](int x, int y, int z) {
933 return x < y && y < z;
934 }, a, b, c);
935
936 EXPECT_FALSE(result);
937}
938
940{
941 std::vector<int> a = {1, 2, 3};
942 std::vector<int> b = {4, 2, 6};
943
944 bool result = pzip_exists_n(pool, [](int x, int y) {
945 return x == y;
946 }, a, b);
947
948 EXPECT_TRUE(result); // 2 == 2
949}
950
952{
953 std::vector<int> a = {1, 2, 3};
954 std::vector<int> b = {4, 5, 6};
955
956 bool result = pzip_exists_n(pool, [](int x, int y) {
957 return x == y;
958 }, a, b);
959
960 EXPECT_FALSE(result);
961}
962
964{
965 std::vector<int> a = {1, 2, 3, 4, 5};
966 std::vector<int> b = {5, 4, 3, 2, 1};
967
968 size_t count = pzip_count_if_n(pool, [](int x, int y) {
969 return x + y == 6;
970 }, a, b);
971
972 EXPECT_EQ(count, 5u); // All pairs sum to 6
973}
974
976{
977 std::vector<int> a = {1, 2, 3, 4, 5};
978 std::vector<int> b = {1, 2, 3, 4, 5};
979
980 size_t count = pzip_count_if_n(pool, [](int x, int y) {
981 return x == y && x % 2 == 0;
982 }, a, b);
983
984 EXPECT_EQ(count, 2u); // 2 and 4
985}
986
987// =============================================================================
988// Enumerate Tests
989// =============================================================================
990
992{
993 std::vector<int> data(100, 0);
994
995 penumerate_for_each(pool, data, [](size_t i, int& x) {
996 x = static_cast<int>(i * 2);
997 });
998
999 for (size_t i = 0; i < data.size(); ++i)
1000 EXPECT_EQ(data[i], static_cast<int>(i * 2));
1001}
1002
1004{
1005 const std::vector<int> data = {10, 20, 30, 40, 50};
1006 std::atomic<int> weighted_sum{0};
1007
1008 penumerate_for_each(pool, data, [&weighted_sum](size_t i, int x) {
1009 weighted_sum += static_cast<int>(i) * x;
1010 });
1011
1012 // 0*10 + 1*20 + 2*30 + 3*40 + 4*50 = 0 + 20 + 60 + 120 + 200 = 400
1013 EXPECT_EQ(weighted_sum.load(), 400);
1014}
1015
1017{
1018 std::vector<std::string> words = {"a", "bb", "ccc"};
1019
1020 auto result = penumerate_maps(pool, words,
1021 [](size_t i, const std::string& s) {
1022 return std::to_string(i) + ":" + s;
1023 });
1024
1025 EXPECT_EQ(result[0], "0:a");
1026 EXPECT_EQ(result[1], "1:bb");
1027 EXPECT_EQ(result[2], "2:ccc");
1028}
1029
1031{
1032 std::vector<int> empty;
1033
1034 auto result = penumerate_maps(pool, empty,
1035 [](size_t i, int x) { return static_cast<int>(i + x); });
1036
1037 EXPECT_TRUE(result.empty());
1038}
1039
1040// =============================================================================
1041// Large Data Variadic Tests
1042// =============================================================================
1043
1045{
1046 std::vector<int> a(10000), b(10000), c(10000);
1047 std::iota(a.begin(), a.end(), 0);
1048 std::iota(b.begin(), b.end(), 10000);
1049 std::iota(c.begin(), c.end(), 20000);
1050
1051 std::atomic<long long> sum{0};
1052
1053 pzip_for_each_n(pool, [&sum](int x, int y, int z) {
1054 sum += x + y + z;
1055 }, a, b, c);
1056
1057 // Sum of 0..9999 + 10000..19999 + 20000..29999
1058 // = 3 * (0+1+...+9999) + 10000*10000 + 20000*10000
1059 // = 3 * 49995000 + 100000000 + 200000000
1060 // = 149985000 + 300000000 = 449985000
1061 EXPECT_EQ(sum.load(), 449985000LL);
1062}
1063
1065{
1066 std::vector<int> a(10000), b(10000);
1067 std::iota(a.begin(), a.end(), 0);
1068 std::iota(b.begin(), b.end(), 0);
1069
1070 auto result = pzip_maps_n(pool, [](int x, int y) {
1071 return x * y;
1072 }, a, b);
1073
1074 for (size_t i = 0; i < result.size(); ++i)
1075 EXPECT_EQ(result[i], static_cast<int>(i * i));
1076}
1077
1079{
1080 std::vector<int> a(1000), b(1000), c(1000);
1081 std::iota(a.begin(), a.end(), 0);
1082 std::iota(b.begin(), b.end(), 1000);
1083 std::iota(c.begin(), c.end(), 2000);
1084
1085 // Sequential
1086 std::vector<int> seq_result;
1087 for (size_t i = 0; i < a.size(); ++i)
1088 seq_result.push_back(a[i] + b[i] + c[i]);
1089
1090 // Parallel
1091 auto par_result = pzip_maps_n(pool, [](int x, int y, int z) {
1092 return x + y + z;
1093 }, a, b, c);
1094
1096}
1097
1098int main(int argc, char **argv)
1099{
1100 ::testing::InitGoogleTest(&argc, argv);
1101 return RUN_ALL_TESTS();
1102}
Parallel functional programming operations using ThreadPool.
int main()
TEST_F(ParallelTest, PmapsSquare)
long double w
Definition btreepic.C:153
void empty() noexcept
empty the list
Definition htlist.H:1689
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
A reusable thread pool for efficient parallel task execution.
std::vector< int > single_vec
void SetUp() override
std::vector< int > empty_vec
std::vector< int > small_vec
std::vector< int > large_vec
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
static mt19937 rng
Freq_Node * pred
Predecessor node in level-order traversal.
static mpfr_t y
Definition mpfr_mul_d.c:3
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
bool pall(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel all predicate (short-circuit).
bool pnone(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel none predicate.
auto pmaps(ThreadPool &pool, const Container &c, Op op, size_t chunk_size=0)
Parallel map operation.
auto pmin(ThreadPool &pool, const Container &c, size_t chunk_size=0)
Parallel minimum element.
T pzip_foldl_n(ThreadPool &pool, T init, Op op, Combiner combiner, const Containers &... cs)
Parallel fold/reduce over N zipped containers (variadic).
void pzip_for_each(ThreadPool &pool, const Container1 &c1, const Container2 &c2, Op op, size_t chunk_size=0)
Parallel zip + for_each.
auto penumerate_maps(ThreadPool &pool, const Container &c, Op op, size_t chunk_size=0)
Parallel enumerate with map.
void penumerate_for_each(ThreadPool &pool, Container &c, Op op, size_t chunk_size=0)
Parallel for_each with index (enumerate).
bool pzip_all_n(ThreadPool &pool, Pred pred, const Containers &... cs)
Parallel all predicate over N zipped containers (variadic).
size_t pcount_if(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel count_if operation.
std::optional< size_t > pfind(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel find operation (returns index).
bool pzip_exists_n(ThreadPool &pool, Pred pred, const Containers &... cs)
Parallel exists predicate over N zipped containers (variadic).
void psort(ThreadPool &pool, Container &c, Compare cmp=Compare{}, const size_t min_parallel_size=1024)
Parallel sort (in-place).
void pfor_each(ThreadPool &pool, Container &c, Op op, size_t chunk_size=0)
Parallel for_each operation.
auto pfilter(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel filter operation.
auto ppartition(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel partition (stable).
T pproduct(ThreadPool &pool, const Container &c, T init=T{1}, size_t chunk_size=0)
Parallel product of elements.
T product(const Container &container, const T &init=T{1})
Compute product of all elements.
auto pmax(ThreadPool &pool, const Container &c, size_t chunk_size=0)
Parallel maximum element.
void pzip_for_each_n(ThreadPool &pool, Op op, const Containers &... cs)
Parallel for_each over N zipped containers (variadic).
auto pzip_maps_n(ThreadPool &pool, Op op, const Containers &... cs)
Parallel map over N zipped containers (variadic).
T pfoldl(ThreadPool &pool, const Container &c, T init, BinaryOp op, size_t chunk_size=0)
Parallel left fold (reduce).
auto pfind_value(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel find with value return.
auto pzip_maps(ThreadPool &pool, const Container1 &c1, const Container2 &c2, Op op, size_t chunk_size=0)
Parallel zip + map.
T psum(ThreadPool &pool, const Container &c, T init=T{}, size_t chunk_size=0)
Parallel sum of elements.
auto pminmax(ThreadPool &pool, const Container &c, size_t chunk_size=0)
Parallel min and max elements.
size_t pzip_count_if_n(ThreadPool &pool, Pred pred, const Containers &... cs)
Parallel count over N zipped containers (variadic).
bool pexists(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel exists predicate (short-circuit).
DynList< T > maps(const C &c, Op op)
Classic map operation.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
#define LL
Definition ran_array.c:24