mesytec-mnode/external/taskflow-3.8.0/unittests/test_transform.cpp
2025-01-04 01:25:05 +01:00

553 lines
16 KiB
C++

#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest.h>
#include <taskflow/taskflow.hpp>
#include <taskflow/algorithm/transform.hpp>
// ----------------------------------------------------------------------------
// Parallel Transform 1
// ----------------------------------------------------------------------------
template<typename T, typename P>
void parallel_transform(size_t W) {
std::srand(static_cast<unsigned int>(time(NULL)));
tf::Taskflow taskflow;
tf::Executor executor(W);
for(size_t N=0; N<1000; N=(N+1)*2) {
for(size_t c : {0, 1, 3, 7, 99}) {
taskflow.clear();
typename T::const_iterator src_beg;
typename T::const_iterator src_end;
std::list<std::string>::iterator tgt_beg;
T src;
std::list<std::string> tgt;
taskflow.clear();
auto from = taskflow.emplace([&](){
src.resize(N);
for(auto& d : src) {
d = ::rand() % 10;
tgt.emplace_back("hi");
}
src_beg = src.begin();
src_end = src.end();
tgt_beg = tgt.begin();
});
auto to = taskflow.transform(
std::ref(src_beg), std::ref(src_end), std::ref(tgt_beg),
[] (const auto& in) {
return std::to_string(in+10);
},
P(c)
);
from.precede(to);
executor.run(taskflow).wait();
auto s_itr = src.begin();
auto d_itr = tgt.begin();
while(s_itr != src.end()) {
REQUIRE(*d_itr++ == std::to_string(*s_itr++ + 10));
}
}
}
}
// guided
TEST_CASE("ParallelTransform.Guided.1thread") {
parallel_transform<std::vector<int>, tf::GuidedPartitioner<>>(1);
parallel_transform<std::list<int>, tf::GuidedPartitioner<>>(1);
}
TEST_CASE("ParallelTransform.Guided.2threads") {
parallel_transform<std::vector<int>, tf::GuidedPartitioner<>>(2);
parallel_transform<std::list<int>, tf::GuidedPartitioner<>>(2);
}
TEST_CASE("ParallelTransform.Guided.3threads") {
parallel_transform<std::vector<int>, tf::GuidedPartitioner<>>(3);
parallel_transform<std::list<int>, tf::GuidedPartitioner<>>(3);
}
TEST_CASE("ParallelTransform.Guided.4threads") {
parallel_transform<std::vector<int>, tf::GuidedPartitioner<>>(4);
parallel_transform<std::list<int>, tf::GuidedPartitioner<>>(4);
}
// random
TEST_CASE("ParallelTransform.Random.1thread") {
parallel_transform<std::vector<int>, tf::RandomPartitioner<>>(1);
parallel_transform<std::list<int>, tf::RandomPartitioner<>>(1);
}
TEST_CASE("ParallelTransform.Random.2threads") {
parallel_transform<std::vector<int>, tf::RandomPartitioner<>>(2);
parallel_transform<std::list<int>, tf::RandomPartitioner<>>(2);
}
TEST_CASE("ParallelTransform.Random.3threads") {
parallel_transform<std::vector<int>, tf::RandomPartitioner<>>(3);
parallel_transform<std::list<int>, tf::RandomPartitioner<>>(3);
}
TEST_CASE("ParallelTransform.Random.4threads") {
parallel_transform<std::vector<int>, tf::RandomPartitioner<>>(4);
parallel_transform<std::list<int>, tf::RandomPartitioner<>>(4);
}
// static
TEST_CASE("ParallelTransform.Static.1thread") {
parallel_transform<std::vector<int>, tf::StaticPartitioner<>>(1);
parallel_transform<std::list<int>, tf::StaticPartitioner<>>(1);
}
TEST_CASE("ParallelTransform.Static.2threads") {
parallel_transform<std::vector<int>, tf::StaticPartitioner<>>(2);
parallel_transform<std::list<int>, tf::StaticPartitioner<>>(2);
}
TEST_CASE("ParallelTransform.Static.3threads") {
parallel_transform<std::vector<int>, tf::StaticPartitioner<>>(3);
parallel_transform<std::list<int>, tf::StaticPartitioner<>>(3);
}
TEST_CASE("ParallelTransform.Static.4threads") {
parallel_transform<std::vector<int>, tf::StaticPartitioner<>>(4);
parallel_transform<std::list<int>, tf::StaticPartitioner<>>(4);
}
// ----------------------------------------------------------------------------
// Parallel Transform 2
// ----------------------------------------------------------------------------
template<typename T, typename P>
void parallel_transform2(size_t W) {
std::srand(static_cast<unsigned int>(time(NULL)));
tf::Taskflow taskflow;
tf::Executor executor(W);
for(size_t N=0; N<1000; N=(N+1)*2) {
for(size_t c : {0, 1, 3, 7, 99}) {
taskflow.clear();
typename T::const_iterator src_beg;
typename T::const_iterator src_end;
std::list<std::string>::iterator tgt_beg;
T src;
std::list<std::string> tgt;
taskflow.clear();
auto from = taskflow.emplace([&](){
src.resize(N);
for(auto& d : src) {
d = ::rand() % 10;
tgt.emplace_back("hi");
}
src_beg = src.begin();
src_end = src.end();
tgt_beg = tgt.begin();
});
auto to = taskflow.transform(
std::ref(src_beg), std::ref(src_end), std::ref(src_beg), std::ref(tgt_beg),
[] (const auto& in1, const auto& in2) {
return std::to_string(in1 + in2 + 10);
},
P(c)
);
from.precede(to);
executor.run(taskflow).wait();
auto s_itr = src.begin();
auto d_itr = tgt.begin();
while(s_itr != src.end()) {
REQUIRE(*d_itr++ == std::to_string(2 * *s_itr++ + 10));
}
}
}
}
// guided
TEST_CASE("ParallelTransform2.Guided.1thread") {
parallel_transform2<std::vector<int>, tf::GuidedPartitioner<>>(1);
parallel_transform2<std::list<int>, tf::GuidedPartitioner<>>(1);
}
TEST_CASE("ParallelTransform2.Guided.2threads") {
parallel_transform2<std::vector<int>, tf::GuidedPartitioner<>>(2);
parallel_transform2<std::list<int>, tf::GuidedPartitioner<>>(2);
}
TEST_CASE("ParallelTransform2.Guided.3threads") {
parallel_transform2<std::vector<int>, tf::GuidedPartitioner<>>(3);
parallel_transform2<std::list<int>, tf::GuidedPartitioner<>>(3);
}
TEST_CASE("ParallelTransform2.Guided.4threads") {
parallel_transform2<std::vector<int>, tf::GuidedPartitioner<>>(4);
parallel_transform2<std::list<int>, tf::GuidedPartitioner<>>(4);
}
// dynamic
TEST_CASE("ParallelTransform2.Dynamic.1thread") {
parallel_transform2<std::vector<int>, tf::DynamicPartitioner<>>(1);
parallel_transform2<std::list<int>, tf::DynamicPartitioner<>>(1);
}
TEST_CASE("ParallelTransform2.Dynamic.2threads") {
parallel_transform2<std::vector<int>, tf::DynamicPartitioner<>>(2);
parallel_transform2<std::list<int>, tf::DynamicPartitioner<>>(2);
}
TEST_CASE("ParallelTransform2.Dynamic.3threads") {
parallel_transform2<std::vector<int>, tf::DynamicPartitioner<>>(3);
parallel_transform2<std::list<int>, tf::DynamicPartitioner<>>(3);
}
TEST_CASE("ParallelTransform2.Dynamic.4threads") {
parallel_transform2<std::vector<int>, tf::DynamicPartitioner<>>(4);
parallel_transform2<std::list<int>, tf::DynamicPartitioner<>>(4);
}
// static
TEST_CASE("ParallelTransform2.Static.1thread") {
parallel_transform2<std::vector<int>, tf::StaticPartitioner<>>(1);
parallel_transform2<std::list<int>, tf::StaticPartitioner<>>(1);
}
TEST_CASE("ParallelTransform2.Static.2threads") {
parallel_transform2<std::vector<int>, tf::StaticPartitioner<>>(2);
parallel_transform2<std::list<int>, tf::StaticPartitioner<>>(2);
}
TEST_CASE("ParallelTransform2.Static.3threads") {
parallel_transform2<std::vector<int>, tf::StaticPartitioner<>>(3);
parallel_transform2<std::list<int>, tf::StaticPartitioner<>>(3);
}
TEST_CASE("ParallelTransform2.Static.4threads") {
parallel_transform2<std::vector<int>, tf::StaticPartitioner<>>(4);
parallel_transform2<std::list<int>, tf::StaticPartitioner<>>(4);
}
// random
TEST_CASE("ParallelTransform2.Random.1thread") {
parallel_transform2<std::vector<int>, tf::RandomPartitioner<>>(1);
parallel_transform2<std::list<int>, tf::RandomPartitioner<>>(1);
}
TEST_CASE("ParallelTransform2.Random.2threads") {
parallel_transform2<std::vector<int>, tf::RandomPartitioner<>>(2);
parallel_transform2<std::list<int>, tf::RandomPartitioner<>>(2);
}
TEST_CASE("ParallelTransform2.Random.3threads") {
parallel_transform2<std::vector<int>, tf::RandomPartitioner<>>(3);
parallel_transform2<std::list<int>, tf::RandomPartitioner<>>(3);
}
TEST_CASE("ParallelTransform2.Random.4threads") {
parallel_transform2<std::vector<int>, tf::RandomPartitioner<>>(4);
parallel_transform2<std::list<int>, tf::RandomPartitioner<>>(4);
}
// ----------------------------------------------------------------------------
// Parallel Transform 3
// ----------------------------------------------------------------------------
template <typename P>
void parallel_transform3(size_t W) {
std::srand(static_cast<unsigned int>(time(NULL)));
tf::Taskflow taskflow;
tf::Executor executor(W);
using std::string;
using std::size_t;
for(size_t N=0; N<1000; N=(N+1)*2) {
std::multimap<int, size_t> src;
/** Reference implementation with std::transform */
std::vector<string> ref;
/** Target implementation with Subflow::transform */
std::vector<string> tgt;
typename std::vector<string>::iterator tgt_beg;
/** A generic function to cast integers to string */
const auto myFunction = [](const size_t x) -> string {
return "id_" + std::to_string(x);
};
taskflow.clear();
/** Group integers 0..(N-1) into ten groups,
* each having an unique key `d`.
*/
auto from = taskflow.emplace([&, N](){
for(size_t i = 0; i < N; i++) {
const int d = ::rand() % 10;
src.emplace(d, i);
}
ref.resize(N);
tgt.resize(N);
tgt_beg = tgt.begin();
});
auto to_ref = taskflow.emplace([&]() {
// Find entries matching key = 0.
// This can return empty results.
const auto [src_beg, src_end] = src.equal_range(0);
const size_t n_matching = std::distance(src_beg, src_end);
ref.resize(n_matching);
// Extract all values having matching key value.
std::transform(src_beg, src_end, ref.begin(),
[&](const auto& x) -> string {
return myFunction(x.second);
}
);
});
/** Dynamic scheduling with Subflow::transform */
auto to_tgt = taskflow.emplace([&](tf::Subflow& subflow) {
// Find entries matching key = 0
const auto [src_beg, src_end] = src.equal_range(0);
const size_t n_matching = std::distance(src_beg, src_end);
tgt.resize(n_matching);
subflow.transform(
std::ref(src_beg), std::ref(src_end), std::ref(tgt_beg),
[&] (const auto& x) -> string {
return myFunction(x.second);
}, P());
subflow.join();
});
from.precede(to_ref);
from.precede(to_tgt);
executor.run(taskflow).wait();
/** Target entries much match. */
REQUIRE(std::equal(tgt.begin(), tgt.end(), ref.begin()));
}
}
// guided
TEST_CASE("ParallelTransform3.Guided.1thread") {
parallel_transform3<tf::GuidedPartitioner<>>(1);
parallel_transform3<tf::GuidedPartitioner<>>(1);
}
TEST_CASE("ParallelTransform3.Guided.2threads") {
parallel_transform3<tf::GuidedPartitioner<>>(2);
parallel_transform3<tf::GuidedPartitioner<>>(2);
}
TEST_CASE("ParallelTransform3.Guided.3threads") {
parallel_transform3<tf::GuidedPartitioner<>>(3);
parallel_transform3<tf::GuidedPartitioner<>>(3);
}
TEST_CASE("ParallelTransform3.Guided.4threads") {
parallel_transform3<tf::GuidedPartitioner<>>(4);
parallel_transform3<tf::GuidedPartitioner<>>(4);
}
// dynamic
TEST_CASE("ParallelTransform3.Dynamic.1thread") {
parallel_transform3<tf::DynamicPartitioner<>>(1);
parallel_transform3<tf::DynamicPartitioner<>>(1);
}
TEST_CASE("ParallelTransform3.Dynamic.2threads") {
parallel_transform3<tf::DynamicPartitioner<>>(2);
parallel_transform3<tf::DynamicPartitioner<>>(2);
}
TEST_CASE("ParallelTransform3.Dynamic.3threads") {
parallel_transform3<tf::DynamicPartitioner<>>(3);
parallel_transform3<tf::DynamicPartitioner<>>(3);
}
TEST_CASE("ParallelTransform3.Dynamic.4threads") {
parallel_transform3<tf::DynamicPartitioner<>>(4);
parallel_transform3<tf::DynamicPartitioner<>>(4);
}
// static
TEST_CASE("ParallelTransform3.Static.1thread") {
parallel_transform3<tf::StaticPartitioner<>>(1);
parallel_transform3<tf::StaticPartitioner<>>(1);
}
TEST_CASE("ParallelTransform3.Static.2threads") {
parallel_transform3<tf::StaticPartitioner<>>(2);
parallel_transform3<tf::StaticPartitioner<>>(2);
}
TEST_CASE("ParallelTransform3.Static.3threads") {
parallel_transform3<tf::StaticPartitioner<>>(3);
parallel_transform3<tf::StaticPartitioner<>>(3);
}
TEST_CASE("ParallelTransform3.Static.4threads") {
parallel_transform3<tf::StaticPartitioner<>>(4);
parallel_transform3<tf::StaticPartitioner<>>(4);
}
// random
TEST_CASE("ParallelTransform3.Random.1thread") {
parallel_transform3<tf::RandomPartitioner<>>(1);
parallel_transform3<tf::RandomPartitioner<>>(1);
}
TEST_CASE("ParallelTransform3.Random.2threads") {
parallel_transform3<tf::RandomPartitioner<>>(2);
parallel_transform3<tf::RandomPartitioner<>>(2);
}
TEST_CASE("ParallelTransform3.Random.3threads") {
parallel_transform3<tf::RandomPartitioner<>>(3);
parallel_transform3<tf::RandomPartitioner<>>(3);
}
TEST_CASE("ParallelTransform3.Random.4threads") {
parallel_transform3<tf::RandomPartitioner<>>(4);
parallel_transform3<tf::RandomPartitioner<>>(4);
}
// ----------------------------------------------------------------------------
// Closure Wrapper
// ----------------------------------------------------------------------------
int& GetThreadSpecificContext()
{
thread_local int context = 0;
return context;
}
const int UPPER = 1000;
TEST_CASE("ClosureWrapper.transform.Static" * doctest::timeout(300))
{
// Write a test case for using the taskwrapper on tf::transform
for (int tc = 1; tc < 16; tc++)
{
tf::Executor executor(tc);
std::atomic<int> wrapper_called_count = 0;
tf::Taskflow taskflow;
std::vector<int> range(UPPER, 0);
std::vector<int> result(UPPER);
taskflow.transform(range.begin(), range.end(), begin(result),
[&](int) {
return GetThreadSpecificContext();
},
tf::StaticPartitioner(1, [&](auto&& task){
wrapper_called_count++;
GetThreadSpecificContext() = tc;
task();
GetThreadSpecificContext() = 0;
})
);
executor.run(taskflow).wait();
REQUIRE(wrapper_called_count == tc);
REQUIRE(result == std::vector<int>(UPPER, tc));
}
}
// Implement for dynamic case for transform
TEST_CASE("ClosureWrapper.transform.Dynamic" * doctest::timeout(300))
{
for (int tc = 1; tc < 16; tc++)
{
tf::Executor executor(tc);
std::atomic<int> wrapper_called_count = 0;
tf::Taskflow taskflow;
std::vector<int> range(UPPER);
std::iota(range.begin(), range.end(), 0);
std::vector<int> result(UPPER);
taskflow.transform(range.begin(), range.end(), begin(result),
[&](int){
return GetThreadSpecificContext();
},
tf::DynamicPartitioner(1, [&](auto&& task) {
wrapper_called_count++;
GetThreadSpecificContext() = tc;
task();
GetThreadSpecificContext() = 0;
})
);
executor.run(taskflow).wait();
REQUIRE(wrapper_called_count <= tc);
REQUIRE(result == std::vector<int>(UPPER, tc));
}
}
//// ----------------------------------------------------------------------------
//// ParallelTransform Exception
//// ----------------------------------------------------------------------------
//
//void parallel_transform_exception(unsigned W) {
// tf::Taskflow taskflow;
// tf::Executor executor(W);
//
// std::vector<int> src(1000000, 0);
// std::vector<int> tgt(1000000, 0);
//
// taskflow.transform(src.begin(), src.end(), tgt.begin(), [](int&) {
// throw std::runtime_error("x");
// return 1;
// });
// REQUIRE_THROWS_WITH_AS(executor.run(taskflow).get(), "x", std::runtime_error);
//}
//
//TEST_CASE("ParallelTransform.Exception.1thread") {
// parallel_transform_exception(1);
//}
//
//TEST_CASE("ParallelTransform.Exception.2threads") {
// parallel_transform_exception(2);
//}
//
//TEST_CASE("ParallelTransform.Exception.3threads") {
// parallel_transform_exception(3);
//}
//
//TEST_CASE("ParallelTransform.Exception.4threads") {
// parallel_transform_exception(4);
//}