/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ #pragma once #ifndef STENCIL_HPP #define STENCIL_HPP #include #include #include #include #include #include #include #include // importand order in includes #include #include #include "task.hpp" #include "threadPool.hpp" template class Stencil : public ff::ff_Map> { public: Stencil(std::function)> convolution, std::vector> neighborhood, int iterations); Stencil(std::function)> convolution, std::vector> neighborhood, int iterations, int maxworkers); Task *svc(Task *); std::vector> *operator()(std::vector> *matrix, int iterations); void stdthread(std::vector *>> *ResultsVector, std::vector *> *> *OutputVector); void sequential(std::vector *>> *ResultsVector, std::vector *> *> *OutputVector); private: Task *svc_helper(Task *t, int iterations); void constructor_helper(std::vector> neighborhood); std::function)> Convolution; std::vector> Neighborhood; int Iterations = 0; std::vector Borders{0, 0, 0, 0}; // left, top, right, bottom int MaxWorkers = 0; }; template Stencil::Stencil(std::function)> convolution, std::vector> neighborhood, int iterations) : ff::ff_Map>(), Convolution(convolution), Neighborhood({{0, 0}}), Iterations(iterations) { constructor_helper(neighborhood); } template Stencil::Stencil(std::function)> convolution, std::vector> neighborhood, int iterations, int maxworkers) : ff::ff_Map>(maxworkers), Convolution(convolution), Neighborhood({{0, 0}}), Iterations(iterations), MaxWorkers(maxworkers) { constructor_helper(neighborhood); } template void Stencil::constructor_helper( std::vector> neighborhood) { // copies neighborhood and adds the default element (0,0) Neighborhood.insert(Neighborhood.end(), neighborhood.begin(), neighborhood.end()); // finds the boundaries of the neighborhood auto frst = [](std::pair a, std::pair b) { return a.first < b.first; }; auto scnd = [](std::pair a, std::pair b) { return a.second < b.second; }; if (neighborhood.size() > 0) { Borders[0] = std::abs(std::min(0, (*std::min_element(Neighborhood.begin(), Neighborhood.end(), frst)) .first)); Borders[1] = std::abs(std::min(0, (*std::min_element(Neighborhood.begin(), Neighborhood.end(), scnd)) .second)); Borders[2] = std::abs(std::max(0, (*std::max_element(Neighborhood.begin(), Neighborhood.end(), frst)) .first)); Borders[3] = std::abs(std::max(0, (*std::max_element(Neighborhood.begin(), Neighborhood.end(), scnd)) .second)); } } // svc function for fastflow library template Task *Stencil::svc(Task *task) { task = svc_helper(task, this->Iterations); ff::ff_node::ff_send_out(task); return this->GO_ON; } // operator to apply to vector> template std::vector> * Stencil::operator()(std::vector> *matrix, int iterations) { if ((*matrix).size() == 0 || (*matrix)[0].size() == 0) { return matrix; } std::vector> * arena = new std::vector>(); *arena = *matrix; Task *task = new Task(arena, (*arena).size(), (*arena)[0].size()); task = svc_helper(task, iterations); *matrix = *task->VectorData; delete task; return matrix; } // function for std thread // ResultsVector: vector of futures where another thread will put the tasks // OutputVector: where to put the processed tasks template void Stencil::stdthread( std::vector *>> *ResultsVector, std::vector *> *> *OutputVector) { if (!ResultsVector || !OutputVector) { std::cerr << "Error: input is NULL [Stencil::stdthread]" << std::endl; return; } ThreadPool thread_pool(MaxWorkers); int MaxThreads = thread_pool.numberOfThreads(); int count = 0; // for each task, create a new arena where to store the new computed values // then send jobs to the threadpool for (auto result : *ResultsVector) { Task *task = result.get(); int niter = Iterations; int delta = task->Rows / MaxThreads; std::vector> *arena = new std::vector>(0); *arena = *(task->VectorData); // copy all from VectorData while (niter > 0) { for (int l = 0; l < MaxThreads - 1; ++l) { thread_pool.addJob([&, l, delta] { for (int x = l * delta; x < (l+1) * delta; ++x) { for (int y = 0; y < task->Cols; ++y) { if (x < Borders[1] || x >= task->Rows - Borders[3] || y < Borders[0] || y >= task->Cols - Borders[2]) { continue; } std::vector n; n.resize(Neighborhood.size()); std::transform( Neighborhood.begin(), Neighborhood.end(), n.begin(), [&task, x, y](std::pair e) { return (*task->VectorData)[x + e.second] [y + e.first]; }); (*arena)[x][y] = Convolution(n); } } }); } thread_pool.addJob([&, delta, MaxThreads] { for (int x = (MaxThreads - 1) * delta; x < task->Rows; ++x) { for (int y = 0; y < task->Cols; ++y) { if (x < Borders[1] || x >= task->Rows - Borders[3] || y < Borders[0] || y >= task->Cols - Borders[2]) { continue; } std::vector n; n.resize(Neighborhood.size()); std::transform( Neighborhood.begin(), Neighborhood.end(), n.begin(), [&task, x, y](std::pair e) { return ( *task->VectorData)[x + e.second][y + e.first]; }); (*arena)[x][y] = Convolution(n); } } }); thread_pool.waitEnd(); std::swap(task->VectorData, arena); --niter; } delete (arena); // set the value of the promise (*OutputVector)[count]->set_value(task); ++count; } } template void Stencil::sequential( std::vector *>> *ResultsVector, std::vector *> *> *OutputVector) { if (!ResultsVector || !OutputVector) { std::cerr << "Error: input is NULL [Stencil::stdthread]" << std::endl; return; } int count = 0; for (auto result : *ResultsVector) { Task *task = result.get(); int niter = Iterations; std::vector> *arena = new std::vector>(0); *arena = *(task->VectorData); // copy all from VectorData while (niter > 0) { for (int x = 0; x < task->Rows; ++x) { for (int y = 0; y < task->Cols; ++y) { if (x < Borders[1] || x >= task->Rows - Borders[3] || y < Borders[0] || y >= task->Cols - Borders[2]) { continue; } std::vector n; n.resize(Neighborhood.size()); std::transform( Neighborhood.begin(), Neighborhood.end(), n.begin(), [&task, x, y](std::pair e) { return ( *task->VectorData)[x + e.second][y + e.first]; }); (*arena)[x][y] = Convolution(n); } } std::swap(task->VectorData, arena); --niter; } delete (arena); (*OutputVector)[count]->set_value(task); ++count; } } template Task *Stencil::svc_helper(Task *task, int iterations) { int niter = iterations; std::vector> *arena = new std::vector>(0); *arena = *(task->VectorData); while (niter > 0) { parallel_for( 0, task->Rows, [&](const int x) { for (int y = 0; y < task->Cols; ++y) { if (x < Borders[1] || x >= task->Rows - Borders[3] || y < Borders[0] || y >= task->Cols - Borders[2]) { continue; } std::vector n; n.resize(Neighborhood.size()); std::transform( Neighborhood.begin(), Neighborhood.end(), n.begin(), [&task, x, y](std::pair e) { return (*task->VectorData)[x + e.second][y + e.first]; }); (*arena)[x][y] = Convolution(n); } }, MaxWorkers); std::swap(task->VectorData, arena); --niter; } delete (arena); return task; } #endif /* STENCIL_HPP */