280 lines
10 KiB
C++
280 lines
10 KiB
C++
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
#pragma once
|
|
#ifndef STENCIL_HPP
|
|
#define STENCIL_HPP
|
|
|
|
#include <algorithm>
|
|
#include <cstdio>
|
|
#include <functional>
|
|
#include <future>
|
|
#include <iostream>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <ff/ff.hpp>
|
|
// importand order in includes
|
|
#include <ff/map.hpp>
|
|
#include <ff/parallel_for.hpp>
|
|
|
|
#include "task.hpp"
|
|
#include "threadPool.hpp"
|
|
|
|
template <typename T> class Stencil : public ff::ff_Map<Task<T>> {
|
|
public:
|
|
Stencil(std::function<T(std::vector<T>)> convolution,
|
|
std::vector<std::pair<int, int>> neighborhood, int iterations);
|
|
Stencil(std::function<T(std::vector<T>)> convolution,
|
|
std::vector<std::pair<int, int>> neighborhood, int iterations,
|
|
int maxworkers);
|
|
Task<T> *svc(Task<T> *);
|
|
std::vector<std::vector<T>> *operator()(std::vector<std::vector<T>> *matrix,
|
|
int iterations);
|
|
void stdthread(std::vector<std::shared_future<Task<T> *>> *ResultsVector,
|
|
std::vector<std::promise<Task<T> *> *> *OutputVector);
|
|
void sequential(std::vector<std::shared_future<Task<T> *>> *ResultsVector,
|
|
std::vector<std::promise<Task<T> *> *> *OutputVector);
|
|
|
|
private:
|
|
Task<T> *svc_helper(Task<T> *t);
|
|
void constructor_helper(std::vector<std::pair<int, int>> neighborhood);
|
|
|
|
std::function<T(std::vector<T>)> Convolution;
|
|
std::vector<std::pair<int, int>> Neighborhood;
|
|
int Iterations = 0;
|
|
std::vector<int> Borders{0, 0, 0, 0}; // left, top, right, bottom
|
|
int MaxWorkers = 0;
|
|
};
|
|
|
|
template <typename T>
|
|
Stencil<T>::Stencil(std::function<T(std::vector<T>)> convolution,
|
|
std::vector<std::pair<int, int>> neighborhood,
|
|
int iterations)
|
|
: ff::ff_Map<Task<T>>(), Convolution(convolution), Neighborhood({{0, 0}}),
|
|
Iterations(iterations) {
|
|
constructor_helper(neighborhood);
|
|
}
|
|
|
|
template <typename T>
|
|
Stencil<T>::Stencil(std::function<T(std::vector<T>)> convolution,
|
|
std::vector<std::pair<int, int>> neighborhood,
|
|
int iterations, int maxworkers)
|
|
: ff::ff_Map<Task<T>>(maxworkers), Convolution(convolution),
|
|
Neighborhood({{0, 0}}), Iterations(iterations), MaxWorkers(maxworkers) {
|
|
constructor_helper(neighborhood);
|
|
}
|
|
|
|
template <typename T>
|
|
void Stencil<T>::constructor_helper(
|
|
std::vector<std::pair<int, int>> 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<int, int> a, std::pair<int, int> b) {
|
|
return a.first < b.first;
|
|
};
|
|
auto scnd = [](std::pair<int, int> a, std::pair<int, int> 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 <typename T> Task<T> *Stencil<T>::svc(Task<T> *task) {
|
|
task = svc_helper(task);
|
|
ff::ff_node::ff_send_out(task);
|
|
return this->GO_ON;
|
|
}
|
|
|
|
// operator to apply to vector<vector<T>>
|
|
template <typename T>
|
|
std::vector<std::vector<T>> *
|
|
Stencil<T>::operator()(std::vector<std::vector<T>> *matrix, int iterations) {
|
|
if ((*matrix).size() == 0 || (*matrix)[0].size() == 0) {
|
|
return matrix;
|
|
}
|
|
Task<T> *task = new Task<T>(matrix, (*matrix).size(), (*matrix)[0].size());
|
|
task = svc_helper(task);
|
|
return task->VectorData;
|
|
}
|
|
|
|
// function for std thread
|
|
// ResultsVector: vector of futures where another thread will put the tasks
|
|
// OutputVector: where to put the processed tasks
|
|
template <typename T>
|
|
void Stencil<T>::stdthread(
|
|
std::vector<std::shared_future<Task<T> *>> *ResultsVector,
|
|
std::vector<std::promise<Task<T> *> *> *OutputVector) {
|
|
if (!ResultsVector || !OutputVector) {
|
|
std::cerr << "Error: input is NULL [Stencil<T>::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<T> *task = result.get();
|
|
int niter = Iterations;
|
|
int delta = task->Rows / MaxThreads;
|
|
|
|
std::vector<std::vector<T>> *arena = new std::vector<std::vector<T>>(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<T> n;
|
|
n.resize(Neighborhood.size());
|
|
std::transform(
|
|
Neighborhood.begin(), Neighborhood.end(),
|
|
n.begin(),
|
|
[&task, x, y](std::pair<int, int> 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<T> n;
|
|
n.resize(Neighborhood.size());
|
|
std::transform(
|
|
Neighborhood.begin(), Neighborhood.end(), n.begin(),
|
|
[&task, x, y](std::pair<int, int> 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 <typename T>
|
|
void Stencil<T>::sequential(
|
|
std::vector<std::shared_future<Task<T> *>> *ResultsVector,
|
|
std::vector<std::promise<Task<T> *> *> *OutputVector) {
|
|
if (!ResultsVector || !OutputVector) {
|
|
std::cerr << "Error: input is NULL [Stencil<T>::stdthread]"
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
int count = 0;
|
|
|
|
for (auto result : *ResultsVector) {
|
|
Task<T> *task = result.get();
|
|
int niter = Iterations;
|
|
|
|
std::vector<std::vector<T>> *arena = new std::vector<std::vector<T>>(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<T> n;
|
|
n.resize(Neighborhood.size());
|
|
std::transform(
|
|
Neighborhood.begin(), Neighborhood.end(), n.begin(),
|
|
[&task, x, y](std::pair<int, int> 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 <typename T> Task<T> *Stencil<T>::svc_helper(Task<T> *task) {
|
|
int niter = Iterations;
|
|
|
|
std::vector<std::vector<T>> *arena = new std::vector<std::vector<T>>(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<T> n;
|
|
n.resize(Neighborhood.size());
|
|
std::transform(
|
|
Neighborhood.begin(), Neighborhood.end(), n.begin(),
|
|
[&task, x, y](std::pair<int, int> 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 */
|