#pragma once #include "../executor.hpp" namespace tf { // ---------------------------------------------------------------------------- // default reduction // ---------------------------------------------------------------------------- template Task FlowBuilder::reduce( B&& beg, E&& end, T& init, O&& bop ) { //return reduce_guided( // std::forward(beg), // std::forward(end), // init, // std::forward(bop), // 1 //); //} // ---------------------------------------------------------------------------- // guided partition // ---------------------------------------------------------------------------- //template //Task FlowBuilder::reduce_guided( // B&& beg, E&& end, T& init, O&& bop, H&& chunk_size //) { using I = stateful_iterator_t; using namespace std::string_literals; Task task = emplace( [b=std::forward(beg), e=std::forward(end), &r=init, o=std::forward(bop) //c=std::forward(chunk_size) ] (Subflow& sf) mutable { // fetch the iterator values I beg = b; I end = e; if(beg == end) { return; } //size_t C = (c == 0) ? 1 : c; size_t C = 1; size_t W = sf._executor.num_workers(); size_t N = std::distance(beg, end); // only myself - no need to spawn another graph if(W <= 1 || N <= C) { for(; beg!=end; r = o(r, *beg++)); return; } if(N < W) { W = N; } std::mutex mutex; std::atomic next(0); for(size_t w=0; w= N) { break; } //sf.emplace([&mutex, &next, &r, beg, N, W, &o, C] () mutable { sf.silent_async([&mutex, &next, &r, beg, N, W, &o, C] () mutable { size_t s0 = next.fetch_add(2, std::memory_order_relaxed); if(s0 >= N) { return; } std::advance(beg, s0); if(N - s0 == 1) { std::lock_guard lock(mutex); r = o(r, *beg); return; } auto beg1 = beg++; auto beg2 = beg++; T sum = o(*beg1, *beg2); size_t z = s0 + 2; size_t p1 = 2 * W * (C + 1); double p2 = 0.5 / static_cast(W); s0 = next.load(std::memory_order_relaxed); while(s0 < N) { size_t r = N - s0; // fine-grained if(r < p1) { while(1) { s0 = next.fetch_add(C, std::memory_order_relaxed); if(s0 >= N) { break; } size_t e0 = (C <= (N - s0)) ? s0 + C : N; std::advance(beg, s0-z); for(size_t x=s0; x(p2 * r); if(q < C) { q = C; } size_t e0 = (q <= r) ? s0 + q : N; if(next.compare_exchange_strong(s0, e0, std::memory_order_acquire, std::memory_order_relaxed)) { std::advance(beg, s0-z); for(size_t x = s0; x lock(mutex); r = o(r, sum); //}).name("prg_"s + std::to_string(w)); }); } sf.join(); }); return task; } // ---------------------------------------------------------------------------- // reduce_dynamic // ---------------------------------------------------------------------------- /*template Task FlowBuilder::reduce_dynamic( B&& beg, E&& end, T& init, O&& bop, H&& chunk_size ) { using I = stateful_iterator_t; using namespace std::string_literals; Task task = emplace( [b=std::forward(beg), e=std::forward(end), &r=init, o=std::forward(bop), c=std::forward(chunk_size) ] (Subflow& sf) mutable { // fetch the iterator values I beg = b; I end = e; if(beg == end) { return; } size_t C = (c == 0) ? 1 : c; size_t W = sf._executor.num_workers(); size_t N = std::distance(beg, end); // only myself - no need to spawn another graph if(W <= 1 || N <= C) { for(; beg!=end; r = o(r, *beg++)); return; } if(N < W) { W = N; } std::mutex mutex; std::atomic next(0); for(size_t w=0; w= N) { break; } //sf.emplace([&mutex, &next, &r, beg, N, &o, C] () mutable { sf.silent_async([&mutex, &next, &r, beg, N, &o, C] () mutable { size_t s0 = next.fetch_add(2, std::memory_order_relaxed); if(s0 >= N) { return; } std::advance(beg, s0); if(N - s0 == 1) { std::lock_guard lock(mutex); r = o(r, *beg); return; } auto beg1 = beg++; auto beg2 = beg++; T sum = o(*beg1, *beg2); size_t z = s0 + 2; while(1) { s0 = next.fetch_add(C, std::memory_order_relaxed); if(s0 >= N) { break; } size_t e0 = (C <= (N - s0)) ? s0 + C : N; std::advance(beg, s0-z); for(size_t x=s0; x lock(mutex); r = o(r, sum); //}).name("prd_"s + std::to_string(w)); }); } sf.join(); }); return task; } // ---------------------------------------------------------------------------- // reduce_static // ---------------------------------------------------------------------------- template Task FlowBuilder::reduce_static( B&& beg, E&& end, T& init, O&& bop, H&& chunk_size ) { using I = stateful_iterator_t; using namespace std::string_literals; Task task = emplace( [b=std::forward(beg), e=std::forward(end), &r=init, o=std::forward(bop), c=std::forward(chunk_size) ] (Subflow& sf) mutable { // fetch the iterator values I beg = b; I end = e; if(beg == end) { return; } size_t C = c; size_t W = sf._executor.num_workers(); size_t N = std::distance(beg, end); // only myself - no need to spawn another graph if(W <= 1 || N <= C) { for(; beg!=end; r = o(r, *beg++)); return; } std::mutex mutex; std::atomic next(0); // even partition if(C == 0) { const size_t q0 = N / W; const size_t t0 = N % W; for(size_t i=0; i lock(mutex); r = o(r, *beg); return; } auto beg1 = beg++; auto beg2 = beg++; T sum = o(*beg1, *beg2); for(size_t i=2; i lock(mutex); r = o(r, sum); //}).name("prs_"s + std::to_string(i)); }); } } // chunk-by-chunk partition else { for(size_t w=0; w= N) { break; } //sf.emplace([&mutex, &next, &r, beg, end, C, N, W, &o] () mutable { sf.silent_async([&mutex, &next, &r, beg, end, C, N, W, &o] () mutable { size_t trip = W*C; size_t s0 = next.fetch_add(C, std::memory_order_relaxed); std::advance(beg, s0); T sum; if(C == 1) { if(s0 + trip >= N) { // last trip std::lock_guard lock(mutex); r = o(r, *beg); return; } else { // one more trip auto beg1 = beg; auto beg2 = std::next(beg, trip); sum = o(*beg1, *beg2); s0 += trip*2; if(s0 >= N) { goto end_reduce; } beg = std::next(beg2, trip); } } else { if(N - s0 == 1) { std::lock_guard lock(mutex); r = o(r, *beg); return; } auto beg1 = beg++; auto beg2 = beg++; sum = o(*beg1, *beg2); I e = beg; size_t i; for(i=2; i= N) { goto end_reduce; } std::advance(beg, trip-2); } while(1) { size_t i; I e = beg; for(i=0; i= N) { break; } std::advance(beg, trip); } end_reduce: std::lock_guard lock(mutex); r = o(r, sum); //}).name("prs_"s + std::to_string(w)); }); } } sf.join(); }); return task; } */ // ---------------------------------------------------------------------------- // default transform and reduction // ---------------------------------------------------------------------------- template Task FlowBuilder::transform_reduce( B&& beg, E&& end, T& init, BOP&& bop, UOP&& uop ) { // return transform_reduce_guided( // std::forward(beg), // std::forward(end), // init, // std::forward(bop), // std::forward(uop), // 1 // ); //} // ---------------------------------------------------------------------------- // guided partition // ---------------------------------------------------------------------------- //template //Task FlowBuilder::transform_reduce_guided( // B&& beg, E&& end, T& init, BOP&& bop, UOP&& uop, H&& chunk_size //) { using I = stateful_iterator_t; using namespace std::string_literals; Task task = emplace( [b=std::forward(beg), e=std::forward(end), &r=init, bop=std::forward(bop), uop=std::forward(uop) //c=std::forward(chunk_size) ] (Subflow& sf) mutable { // fetch the iterator values I beg = b; I end = e; if(beg == end) { return; } //size_t C = (c == 0) ? 1 : c; size_t C = 1; size_t W = sf._executor.num_workers(); size_t N = std::distance(beg, end); // only myself - no need to spawn another graph if(W <= 1 || N <= C) { for(; beg!=end; r = bop(r, uop(*beg++))); return; } if(N < W) { W = N; } std::mutex mutex; std::atomic next(0); for(size_t w=0; w= N) { break; } //sf.emplace([&mutex, &next, &r, beg, N, W, &bop, &uop, C] () mutable { sf.silent_async([&mutex, &next, &r, beg, N, W, &bop, &uop, C] () mutable { size_t s0 = next.fetch_add(2, std::memory_order_relaxed); if(s0 >= N) { return; } std::advance(beg, s0); if(N - s0 == 1) { std::lock_guard lock(mutex); r = bop(r, uop(*beg)); return; } auto beg1 = beg++; auto beg2 = beg++; T sum = bop(uop(*beg1), uop(*beg2)); size_t z = s0 + 2; size_t p1 = 2 * W * (C + 1); double p2 = 0.5 / static_cast(W); s0 = next.load(std::memory_order_relaxed); while(s0 < N) { size_t r = N - s0; // fine-grained if(r < p1) { while(1) { s0 = next.fetch_add(C, std::memory_order_relaxed); if(s0 >= N) { break; } size_t e0 = (C <= (N - s0)) ? s0 + C : N; std::advance(beg, s0-z); for(size_t x=s0; x(p2 * r); if(q < C) { q = C; } size_t e0 = (q <= r) ? s0 + q : N; if(next.compare_exchange_strong(s0, e0, std::memory_order_acquire, std::memory_order_relaxed)) { std::advance(beg, s0-z); for(size_t x = s0; x lock(mutex); r = bop(r, sum); //}).name("prg_"s + std::to_string(w)); }); } sf.join(); }); return task; } // ---------------------------------------------------------------------------- // transform_reduce_dynamic // ---------------------------------------------------------------------------- /*template Task FlowBuilder::transform_reduce_dynamic( B&& beg, E&& end, T& init, BOP&& bop, UOP&& uop, H&& chunk_size ) { using I = stateful_iterator_t; using namespace std::string_literals; Task task = emplace( [b=std::forward(beg), e=std::forward(end), &r=init, bop=std::forward(bop), uop=std::forward(uop), c=std::forward(chunk_size) ] (Subflow& sf) mutable { // fetch the iterator values I beg = b; I end = e; if(beg == end) { return; } size_t C = (c == 0) ? 1 : c; size_t W = sf._executor.num_workers(); size_t N = std::distance(beg, end); // only myself - no need to spawn another graph if(W <= 1 || N <= C) { for(; beg!=end; r = bop(r, uop(*beg++))); return; } if(N < W) { W = N; } std::mutex mutex; std::atomic next(0); for(size_t w=0; w= N) { break; } //sf.emplace([&mutex, &next, &r, beg, N, &bop, &uop, C] () mutable { sf.silent_async([&mutex, &next, &r, beg, N, &bop, &uop, C] () mutable { size_t s0 = next.fetch_add(2, std::memory_order_relaxed); if(s0 >= N) { return; } std::advance(beg, s0); if(N - s0 == 1) { std::lock_guard lock(mutex); r = bop(r, uop(*beg)); return; } auto beg1 = beg++; auto beg2 = beg++; T sum = bop(uop(*beg1), uop(*beg2)); size_t z = s0 + 2; while(1) { s0 = next.fetch_add(C, std::memory_order_relaxed); if(s0 >= N) { break; } size_t e0 = (C <= (N - s0)) ? s0 + C : N; std::advance(beg, s0-z); for(size_t x=s0; x lock(mutex); r = bop(r, sum); //}).name("prd_"s + std::to_string(w)); }); } sf.join(); }); return task; } // ---------------------------------------------------------------------------- // transform_reduce_static // ---------------------------------------------------------------------------- template Task FlowBuilder::transform_reduce_static( B&& beg, E&& end, T& init, BOP&& bop, UOP&& uop, H&& chunk_size ) { using I = stateful_iterator_t; using namespace std::string_literals; Task task = emplace( [b=std::forward(beg), e=std::forward(end), &r=init, bop=std::forward(bop), uop=std::forward(uop), c=std::forward(chunk_size) ] (Subflow& sf) mutable { // fetch the iterator values I beg = b; I end = e; if(beg == end) { return; } size_t C = c; size_t W = sf._executor.num_workers(); size_t N = std::distance(beg, end); // only myself - no need to spawn another graph if(W <= 1 || N <= C) { for(; beg!=end; r = bop(r, uop(*beg++))); return; } std::mutex mutex; std::atomic next(0); // even partition if(C == 0) { const size_t q0 = N / W; const size_t t0 = N % W; for(size_t i=0; i lock(mutex); r = bop(r, uop(*beg)); return; } auto beg1 = beg++; auto beg2 = beg++; T sum = bop(uop(*beg1), uop(*beg2)); for(size_t i=2; i lock(mutex); r = bop(r, sum); //}).name("prs_"s + std::to_string(i)); }); } } // chunk-by-chunk partition else { for(size_t w=0; w= N) { break; } //sf.emplace([&mutex, &next, &r, beg, end, C, N, W, &bop, &uop] () mutable { sf.silent_async([&mutex, &next, &r, beg, end, C, N, W, &bop, &uop] () mutable { size_t trip = W*C; size_t s0 = next.fetch_add(C, std::memory_order_relaxed); std::advance(beg, s0); T sum; if(C == 1) { if(s0 + trip >= N) { // last trip std::lock_guard lock(mutex); r = bop(r, uop(*beg)); return; } else { // one more trip auto beg1 = beg; auto beg2 = std::next(beg, trip); sum = bop(uop(*beg1), uop(*beg2)); s0 += trip*2; if(s0 >= N) { goto end_transform_reduce; } beg = std::next(beg2, trip); } } else { if(N - s0 == 1) { std::lock_guard lock(mutex); r = bop(r, uop(*beg)); return; } auto beg1 = beg++; auto beg2 = beg++; sum = bop(uop(*beg1), uop(*beg2)); I e = beg; size_t i; for(i=2; i= N) { goto end_transform_reduce; } std::advance(beg, trip-2); } while(1) { size_t i; I e = beg; for(i=0; i= N) { break; } std::advance(beg, trip); } end_transform_reduce: std::lock_guard lock(mutex); r = bop(r, sum); //}).name("prs_"s + std::to_string(w)); }); } } sf.join(); }); return task; }*/ } // end of namespace tf -----------------------------------------------------