37#include <gtest/gtest.h>
80 auto result =
pmaps(pool, small_vec, [](
int x) {
return x * x; });
82 EXPECT_EQ(result, (std::vector<int>{1, 4, 9, 16, 25}));
87 auto result =
pmaps<double>(pool, small_vec, [](
int x) {
return x * 1.5; });
95 auto result =
pmaps(pool, small_vec, [](
int x) {
return std::to_string(x); });
103 auto result =
pmaps(pool, empty_vec, [](
int x) {
return x * 2; });
109 auto result =
pmaps(pool, single_vec, [](
int x) {
return x * 2; });
116 auto result =
pmaps(pool, large_vec, [](
int x) {
return x * 2; });
118 for (
size_t i = 0; i < result.size(); ++i)
119 EXPECT_EQ(result[i],
static_cast<int>((i + 1) * 2));
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}));
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));
142 auto result =
pfilter(pool, small_vec, [](
int x) {
return x % 2 == 0; });
143 EXPECT_EQ(result, (std::vector<int>{2, 4}));
148 auto result =
pfilter(pool, small_vec, [](
int x) {
return x % 2 != 0; });
149 EXPECT_EQ(result, (std::vector<int>{1, 3, 5}));
154 auto result =
pfilter(pool, small_vec, [](
int x) {
return x > 100; });
160 auto result =
pfilter(pool, small_vec, [](
int x) {
return x > 0; });
166 auto result =
pfilter(pool, empty_vec, [](
int) {
return true; });
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}));
179 auto result =
pfilter(pool, large_vec, [](
int x) {
return x % 100 == 0; });
191 int sum =
pfoldl(pool, small_vec, 0, std::plus<int>());
197 int product =
pfoldl(pool, small_vec, 1, std::multiplies<int>());
204 [](
int a,
int b) {
return std::max(a, b); });
210 int result =
pfoldl(pool, empty_vec, 42, std::plus<int>());
217 [](
long long a,
int b) {
return a + b; });
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}));
235 std::atomic<int>
count{0};
237 if (x % 2 == 0) ++
count;
244 std::atomic<int>
count{0};
255 bool result =
pall(pool, small_vec, [](
int x) {
return x > 0; });
261 bool result =
pall(pool, small_vec, [](
int x) {
return x > 3; });
267 bool result =
pall(pool, empty_vec, [](
int) {
return false; });
273 bool result =
pexists(pool, small_vec, [](
int x) {
return x == 3; });
279 bool result =
pexists(pool, small_vec, [](
int x) {
return x > 100; });
285 bool result =
pexists(pool, empty_vec, [](
int) {
return true; });
291 bool result =
pnone(pool, small_vec, [](
int x) {
return x < 0; });
297 bool result =
pnone(pool, small_vec, [](
int x) {
return x == 3; });
307 size_t count =
pcount_if(pool, small_vec, [](
int x) {
return x % 2 == 0; });
313 size_t count =
pcount_if(pool, small_vec, [](
int x) {
return x > 0; });
319 size_t count =
pcount_if(pool, small_vec, [](
int x) {
return x > 100; });
325 size_t count =
pcount_if(pool, large_vec, [](
int x) {
return x % 7 == 0; });
335 auto idx =
pfind(pool, small_vec, [](
int x) {
return x == 3; });
342 auto idx =
pfind(pool, small_vec, [](
int x) {
return x == 100; });
348 std::vector<int> data = {1, 2, 3, 3, 3, 4, 5};
349 auto idx =
pfind(pool, data, [](
int x) {
return x == 3; });
356 auto idx =
pfind(pool, empty_vec, [](
int) {
return true; });
362 std::vector<std::string> words = {
"apple",
"banana",
"cherry"};
364 [](
const std::string& s) {
return s.length() > 5; });
375 auto sum =
psum(pool, small_vec);
381 auto sum =
psum(pool, small_vec, 100);
387 auto sum =
psum(pool, empty_vec);
441 auto result =
pminmax(pool, small_vec);
449 std::vector<int> data = {5, 1, 3, 2, 4};
450 auto result =
pminmax(pool, data);
458 auto result =
pminmax(pool, large_vec);
470 std::vector<int> data = {5, 2, 8, 1, 9, 3, 7, 4, 6};
472 EXPECT_EQ(data, (std::vector<int>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
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}));
484 std::vector<int> data;
491 std::vector<int> data = {42};
498 std::vector<int> data = {1, 2, 3, 4, 5};
500 EXPECT_EQ(data, (std::vector<int>{1, 2, 3, 4, 5}));
505 std::vector<int> data = large_vec;
506 std::mt19937
rng(42);
507 std::shuffle(data.begin(), data.end(),
rng);
509 for (
size_t i = 0; i < data.size(); ++i)
510 EXPECT_EQ(data[i],
static_cast<int>(i + 1));
519 std::vector<int> a = {1, 2, 3};
520 std::vector<int> b = {4, 5, 6};
521 std::atomic<int>
sum{0};
529 std::vector<int> a = {1, 2, 3, 4, 5};
530 std::vector<int> b = {10, 20, 30};
531 std::atomic<int>
sum{0};
540 std::vector<int> b = {1, 2, 3};
541 std::atomic<int>
count{0};
553 std::vector<int> a = {1, 2, 3};
554 std::vector<int> b = {4, 5, 6};
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}));
562 std::vector<int> a = {1, 2, 3};
563 std::vector<double> b = {1.5, 2.5, 3.5};
565 auto result =
pzip_maps(pool, a, b, [](
int x,
double y) {
return x +
y; });
584 auto [
yes,
no] =
ppartition(pool, small_vec, [](
int x) {
return x > 0; });
591 auto [
yes,
no] =
ppartition(pool, small_vec, [](
int x) {
return x < 0; });
598 auto [
yes,
no] =
ppartition(pool, empty_vec, [](
int) {
return true; });
605 std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
619 for (
int x : large_vec)
623 auto par_result =
pmaps(pool, large_vec, [](
int x) {
return x * 2 + 1; });
632 for (
int x : large_vec)
646 for (
int x : large_vec)
651 [](
long long a,
int b) {
return a + b; });
662 std::vector<int> data(1000000);
663 std::iota(data.begin(), data.end(), 0);
666 auto seq_start = std::chrono::high_resolution_clock::now();
671 auto seq_end = std::chrono::high_resolution_clock::now();
672 auto seq_duration = std::chrono::duration_cast<std::chrono::microseconds>(
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>(
685 std::cout <<
"\n=== Benchmark: pmaps ===\n";
686 std::cout <<
"Data size: 1M elements\n";
687 std::cout <<
"Sequential: " <<
seq_duration <<
" μs\n";
689 std::cout <<
"Speedup: " <<
speedup <<
"x\n";
690 std::cout <<
"========================\n\n";
696 if (pool.num_threads() > 1)
702 std::vector<int> data(1000000);
703 std::iota(data.begin(), data.end(), 0);
705 auto pred = [](
int x) {
return x % 7 == 0 || x % 11 == 0; };
708 auto seq_start = std::chrono::high_resolution_clock::now();
713 auto seq_end = std::chrono::high_resolution_clock::now();
714 auto seq_duration = std::chrono::duration_cast<std::chrono::microseconds>(
718 auto par_start = std::chrono::high_resolution_clock::now();
720 auto par_end = std::chrono::high_resolution_clock::now();
721 auto par_duration = std::chrono::duration_cast<std::chrono::microseconds>(
727 std::cout <<
"\n=== Benchmark: pfilter ===\n";
728 std::cout <<
"Data size: 1M elements\n";
729 std::cout <<
"Sequential: " <<
seq_duration <<
" μs\n";
731 std::cout <<
"Speedup: " <<
speedup <<
"x\n";
732 std::cout <<
"==========================\n\n";
737 std::vector<int> data(100000);
738 std::iota(data.begin(), data.end(), 0);
739 std::mt19937
rng(123);
740 std::shuffle(data.begin(), data.end(),
rng);
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>(
752 auto par_start = std::chrono::high_resolution_clock::now();
754 auto par_end = std::chrono::high_resolution_clock::now();
755 auto par_duration = std::chrono::duration_cast<std::chrono::microseconds>(
761 std::cout <<
"\n=== Benchmark: psort ===\n";
762 std::cout <<
"Data size: 100K elements\n";
765 std::cout <<
"Speedup: " <<
speedup <<
"x\n";
766 std::cout <<
"========================\n\n";
776 auto future1 = std::async(std::launch::async, [
this]() {
777 return pmaps(pool, large_vec, [](
int x) {
return x * 2; });
780 auto future2 = std::async(std::launch::async, [
this]() {
781 return pfilter(pool, large_vec, [](
int x) {
return x % 2 == 0; });
784 auto future3 = std::async(std::launch::async, [
this]() {
806 EXPECT_EQ(result, (std::vector<int>{1, 4, 9, 16, 25}));
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}));
819 std::vector<int>
huge(100000);
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};
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};
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};
876 std::vector<int> a = {1, 2, 3};
877 std::vector<int> b = {4, 5, 6};
878 std::vector<int> c = {7, 8, 9};
884 EXPECT_EQ(result, (std::vector<int>{12, 15, 18}));
890 std::vector<int> b = {1, 2, 3};
901 std::vector<int> a = {1, 2, 3};
902 std::vector<int> b = {4, 5, 6};
906 [](
int acc,
int x,
int y) {
return acc + x *
y; },
915 std::vector<int> a = {1, 2, 3};
916 std::vector<int> b = {2, 3, 4};
917 std::vector<int> c = {3, 4, 5};
919 bool result =
pzip_all_n(pool, [](
int x,
int y,
int z) {
920 return x <
y &&
y <
z;
928 std::vector<int> a = {1, 2, 3};
929 std::vector<int> b = {2, 3, 4};
930 std::vector<int> c = {3, 2, 5};
932 bool result =
pzip_all_n(pool, [](
int x,
int y,
int z) {
933 return x <
y &&
y <
z;
941 std::vector<int> a = {1, 2, 3};
942 std::vector<int> b = {4, 2, 6};
953 std::vector<int> a = {1, 2, 3};
954 std::vector<int> b = {4, 5, 6};
965 std::vector<int> a = {1, 2, 3, 4, 5};
966 std::vector<int> b = {5, 4, 3, 2, 1};
977 std::vector<int> a = {1, 2, 3, 4, 5};
978 std::vector<int> b = {1, 2, 3, 4, 5};
981 return x ==
y && x % 2 == 0;
993 std::vector<int> data(100, 0);
996 x =
static_cast<int>(i * 2);
999 for (
size_t i = 0; i < data.size(); ++i)
1000 EXPECT_EQ(data[i],
static_cast<int>(i * 2));
1005 const std::vector<int> data = {10, 20, 30, 40, 50};
1018 std::vector<std::string> words = {
"a",
"bb",
"ccc"};
1021 [](
size_t i,
const std::string& s) {
1022 return std::to_string(i) +
":" + s;
1032 std::vector<int> empty;
1035 [](
size_t i,
int x) {
return static_cast<int>(i + x); });
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);
1051 std::atomic<long long>
sum{0};
1066 std::vector<int> a(10000), b(10000);
1067 std::iota(a.begin(), a.end(), 0);
1068 std::iota(b.begin(), b.end(), 0);
1074 for (
size_t i = 0; i < result.size(); ++i)
1075 EXPECT_EQ(result[i],
static_cast<int>(i * i));
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);
1087 for (
size_t i = 0; i < a.size(); ++i)
1100 ::testing::InitGoogleTest(&
argc,
argv);
Parallel functional programming operations using ThreadPool.
TEST_F(ParallelTest, PmapsSquare)
void empty() noexcept
empty the list
size_t size() const noexcept
Count the number of elements of the list.
A reusable thread pool for efficient parallel task execution.
std::vector< int > single_vec
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.
Freq_Node * pred
Predecessor node in level-order traversal.
Main namespace for Aleph-w library functions.
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.
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.