Compleated Code
This commit is contained in:
29
.gitignore
vendored
29
.gitignore
vendored
@ -1,4 +1,31 @@
|
|||||||
# ---> C++
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
# ---> c++
|
||||||
# Prerequisites
|
# Prerequisites
|
||||||
*.d
|
*.d
|
||||||
|
|
||||||
|
|||||||
37
CMakeLists.txt
Normal file
37
CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(main)
|
||||||
|
|
||||||
|
# exports compile_commands.json for the IDE
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
include_directories(BEFORE SYSTEM ${PROJECT_SOURCE_DIR}/../fastflow/)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
|
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
set(CMAKE_CXX_COMPILER "/usr/bin/g++")
|
||||||
|
set(CMAKE_CXX_FLAGS "-Wall")
|
||||||
|
else()
|
||||||
|
set(CMAKE_CXX_COMPILER "/opt/homebrew/bin/g++-13")
|
||||||
|
set(CMAKE_CXX_FLAGS "-Wall -DNO_DEFAULT_MAPPING")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# set(CMAKE_CXX_FLAGS "-Wall -DNO_DEFAULT_MAPPING -DBLOCKING_MODE")
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -finline-functions")
|
||||||
|
|
||||||
|
|
||||||
|
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
add_library(timer timer.cpp timer.hpp)
|
||||||
|
add_library(threadpool threadPool.cpp threadPool.hpp)
|
||||||
|
target_link_libraries(threadpool Threads::Threads)
|
||||||
|
|
||||||
|
add_executable(main main.cpp reader.hpp writer.hpp task.hpp stencil.hpp)
|
||||||
|
include_directories(main PUBLIC ${PROJECT_SOURCE_DIR}/)
|
||||||
|
target_link_libraries(main Threads::Threads timer threadpool)
|
||||||
1
compile_commands.json
Symbolic link
1
compile_commands.json
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
build/compile_commands.json
|
||||||
297
main.cpp
Normal file
297
main.cpp
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <future>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <numeric>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <ff/ff.hpp>
|
||||||
|
// important ff.hpp before other ff includes
|
||||||
|
#include <ff/pipeline.hpp>
|
||||||
|
|
||||||
|
#include "reader.hpp"
|
||||||
|
#include "stencil.hpp"
|
||||||
|
#include "timer.hpp"
|
||||||
|
#include "writer.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace ff;
|
||||||
|
|
||||||
|
char *getOption(char **begin, char **end, const std::string &option) {
|
||||||
|
char **itr = std::find(begin, end, option);
|
||||||
|
if (itr != end && ++itr != end)
|
||||||
|
return *itr;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long int fastflow(vector<string> images,
|
||||||
|
vector<pair<int, int>> neighborhood, int iterations,
|
||||||
|
std::function<char(std::vector<char>)> f,
|
||||||
|
int maxworkers) {
|
||||||
|
Reader<char> reader(images);
|
||||||
|
Stencil<char> stencil(f, neighborhood, iterations, maxworkers);
|
||||||
|
Writer<char> writer;
|
||||||
|
|
||||||
|
ff::ff_Pipe<> pipe(reader, stencil, writer);
|
||||||
|
|
||||||
|
UTimer timer;
|
||||||
|
timer.start();
|
||||||
|
if (pipe.run_and_wait_end() < 0) {
|
||||||
|
error("running pipeline\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
timer.stop();
|
||||||
|
long long int ret = timer.print("\tElapsed time: ");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long int stdthreads(vector<string> images,
|
||||||
|
vector<pair<int, int>> neighborhood, int iterations,
|
||||||
|
std::function<char(std::vector<char>)> f,
|
||||||
|
int maxworkers) {
|
||||||
|
long num_images = images.size();
|
||||||
|
|
||||||
|
Reader<char> reader(images);
|
||||||
|
Stencil<char> *stencil = // pointer so that we can delete only once and not
|
||||||
|
// when out of scope of the thread
|
||||||
|
new Stencil<char>(f, neighborhood, iterations, maxworkers);
|
||||||
|
Writer<char> writer;
|
||||||
|
|
||||||
|
std::vector<std::promise<Task<char> *> *> *tv =
|
||||||
|
new std::vector<std::promise<Task<char> *> *>();
|
||||||
|
std::vector<std::shared_future<Task<char> *>> *in =
|
||||||
|
new std::vector<std::shared_future<Task<char> *>>();
|
||||||
|
std::vector<std::promise<Task<char> *> *> *out =
|
||||||
|
new std::vector<std::promise<Task<char> *> *>();
|
||||||
|
std::vector<std::shared_future<Task<char> *>> *tvw =
|
||||||
|
new std::vector<std::shared_future<Task<char> *>>();
|
||||||
|
for (int i = 0; i < num_images; ++i) {
|
||||||
|
std::promise<Task<char> *> *p = new std::promise<Task<char> *>();
|
||||||
|
std::future<Task<char> *> f = p->get_future();
|
||||||
|
std::promise<Task<char> *> *o = new std::promise<Task<char> *>();
|
||||||
|
std::future<Task<char> *> l = o->get_future();
|
||||||
|
tv->push_back(p);
|
||||||
|
in->push_back(f.share());
|
||||||
|
out->push_back(o);
|
||||||
|
tvw->push_back(l.share());
|
||||||
|
}
|
||||||
|
|
||||||
|
UTimer timer;
|
||||||
|
timer.start();
|
||||||
|
std::thread readerT(reader, images, tv);
|
||||||
|
std::thread stencilT(&Stencil<char>::stdthread, stencil, in, out);
|
||||||
|
std::thread writerT(writer, tvw);
|
||||||
|
readerT.join();
|
||||||
|
stencilT.join();
|
||||||
|
writerT.join();
|
||||||
|
timer.stop();
|
||||||
|
long long int ret = timer.print("\tElapsed time: ");
|
||||||
|
|
||||||
|
for (int i = 0; i < num_images; ++i) {
|
||||||
|
delete ((*tv)[i]);
|
||||||
|
delete ((*out)[i]);
|
||||||
|
}
|
||||||
|
delete tv;
|
||||||
|
delete in;
|
||||||
|
delete out;
|
||||||
|
delete tvw;
|
||||||
|
|
||||||
|
delete stencil;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long int sequential(vector<string> images,
|
||||||
|
vector<pair<int, int>> neighborhood, int iterations,
|
||||||
|
std::function<char(std::vector<char>)> f) {
|
||||||
|
long num_images = images.size();
|
||||||
|
|
||||||
|
Reader<char> reader(images);
|
||||||
|
Stencil<char> *stencil = // pointer so that we can delete only once and not
|
||||||
|
// when out of scope of the thread
|
||||||
|
new Stencil<char>(f, neighborhood, iterations);
|
||||||
|
Writer<char> writer;
|
||||||
|
|
||||||
|
std::vector<std::promise<Task<char> *> *> *tv =
|
||||||
|
new std::vector<std::promise<Task<char> *> *>();
|
||||||
|
std::vector<std::shared_future<Task<char> *>> *in =
|
||||||
|
new std::vector<std::shared_future<Task<char> *>>();
|
||||||
|
std::vector<std::promise<Task<char> *> *> *out =
|
||||||
|
new std::vector<std::promise<Task<char> *> *>();
|
||||||
|
std::vector<std::shared_future<Task<char> *>> *tvw =
|
||||||
|
new std::vector<std::shared_future<Task<char> *>>();
|
||||||
|
for (int i = 0; i < num_images; ++i) {
|
||||||
|
std::promise<Task<char> *> *p = new std::promise<Task<char> *>();
|
||||||
|
std::future<Task<char> *> f = p->get_future();
|
||||||
|
std::promise<Task<char> *> *o = new std::promise<Task<char> *>();
|
||||||
|
std::future<Task<char> *> l = o->get_future();
|
||||||
|
tv->push_back(p);
|
||||||
|
in->push_back(f.share());
|
||||||
|
out->push_back(o);
|
||||||
|
tvw->push_back(l.share());
|
||||||
|
}
|
||||||
|
|
||||||
|
UTimer timer;
|
||||||
|
timer.start();
|
||||||
|
std::thread readerT(reader, images, tv);
|
||||||
|
std::thread stencilT(&Stencil<char>::sequential, stencil, in, out);
|
||||||
|
std::thread writerT(writer, tvw);
|
||||||
|
readerT.join();
|
||||||
|
stencilT.join();
|
||||||
|
writerT.join();
|
||||||
|
timer.stop();
|
||||||
|
long long int ret = timer.print("\tElapsed time: ");
|
||||||
|
|
||||||
|
for (int i = 0; i < num_images; ++i) {
|
||||||
|
delete ((*tv)[i]);
|
||||||
|
delete ((*out)[i]);
|
||||||
|
}
|
||||||
|
delete tv;
|
||||||
|
delete in;
|
||||||
|
delete out;
|
||||||
|
delete tvw;
|
||||||
|
|
||||||
|
delete stencil;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char gameOfLife(vector<char> in) {
|
||||||
|
auto v = std::accumulate(in.begin() + 1, in.end(), 0);
|
||||||
|
if (in[0] && v < 2) {
|
||||||
|
return 0;
|
||||||
|
} else if (in[0] && v > 3) {
|
||||||
|
return 0;
|
||||||
|
} else if (!in[0] && v != 3) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int average_max = 5;
|
||||||
|
int iter_for_num_workers = 128;
|
||||||
|
|
||||||
|
vector<string> images = {
|
||||||
|
"../tests/empty2x2",
|
||||||
|
"../tests/increasing4x6",
|
||||||
|
"../tests/increasing300x200",
|
||||||
|
"../tests/random400x2500",
|
||||||
|
// "../tests/equation",
|
||||||
|
// "../tests/equation2"
|
||||||
|
};
|
||||||
|
|
||||||
|
vector<pair<int, int>> neig = {make_pair(-1, 1), make_pair(-1, 0),
|
||||||
|
make_pair(-1, -1), make_pair(0, 1),
|
||||||
|
make_pair(0, -1), make_pair(1, 1),
|
||||||
|
make_pair(1, 0), make_pair(1, -1)};
|
||||||
|
|
||||||
|
ofstream csvfile;
|
||||||
|
csvfile.open("performance.csv");
|
||||||
|
|
||||||
|
for (std::string image : images) {
|
||||||
|
cout << endl
|
||||||
|
<< "\033[1;31mProcessing: \t" << image << "\033[0m" << endl;
|
||||||
|
|
||||||
|
csvfile << ",Name,Size(B)\n";
|
||||||
|
csvfile << "Image," << image << ",";
|
||||||
|
csvfile << std::filesystem::file_size(image) << "\n";
|
||||||
|
|
||||||
|
csvfile << "\n";
|
||||||
|
csvfile << "Number of iterations,1,2,4,8,16\n";
|
||||||
|
csvfile << "fastflow:,";
|
||||||
|
|
||||||
|
cout << "\033[1;31mFastflow\033[0m" << endl;
|
||||||
|
for (int iterations : {1, 2, 4, 8, 16}) {
|
||||||
|
vector<long long int> results;
|
||||||
|
for (int i = 0; i < average_max; ++i) {
|
||||||
|
results.push_back(
|
||||||
|
fastflow({image}, neig, iterations, &gameOfLife, 0));
|
||||||
|
}
|
||||||
|
csvfile << std::accumulate(results.begin(), results.end(), 0) /
|
||||||
|
average_max
|
||||||
|
<< ",";
|
||||||
|
}
|
||||||
|
csvfile << "\n";
|
||||||
|
|
||||||
|
csvfile << "stdthread:,";
|
||||||
|
cout << "\033[1;31mStdthread\033[0m" << endl;
|
||||||
|
for (int iterations : {1, 2, 4, 8, 16}) {
|
||||||
|
vector<long long int> results;
|
||||||
|
for (int i = 0; i < average_max; ++i) {
|
||||||
|
results.push_back(
|
||||||
|
stdthreads({image}, neig, iterations, &gameOfLife, 0));
|
||||||
|
}
|
||||||
|
csvfile << std::accumulate(results.begin(), results.end(), 0) /
|
||||||
|
average_max
|
||||||
|
<< ",";
|
||||||
|
}
|
||||||
|
csvfile << "\n";
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
csvfile << "Number of Workers,sequential,fastflow,stdthreads,";
|
||||||
|
csvfile << "Iterations:," << iter_for_num_workers << "\n";
|
||||||
|
cout << "\033[1;31mDifferent number of workers\033[0m" << endl;
|
||||||
|
|
||||||
|
int hardware_concurrency = std::thread::hardware_concurrency();
|
||||||
|
|
||||||
|
vector<int> list_max_workers = vector<int>();
|
||||||
|
for (int i = 1; i < hardware_concurrency; i*=2) {
|
||||||
|
list_max_workers.push_back(i);
|
||||||
|
}
|
||||||
|
list_max_workers.push_back(hardware_concurrency);
|
||||||
|
|
||||||
|
for (int max_workers : list_max_workers) {
|
||||||
|
csvfile << max_workers << ",";
|
||||||
|
|
||||||
|
if(max_workers == 1) {
|
||||||
|
cout << "\033[1;31mSequential\033[0m" << endl;
|
||||||
|
vector<long long int> results;
|
||||||
|
for (int i = 0; i < average_max; ++i) {
|
||||||
|
results.push_back(sequential(
|
||||||
|
{image}, neig, iter_for_num_workers, &gameOfLife));
|
||||||
|
}
|
||||||
|
csvfile << std::accumulate(results.begin(), results.end(), 0) /
|
||||||
|
average_max;
|
||||||
|
}
|
||||||
|
csvfile << ",";
|
||||||
|
|
||||||
|
cout << "\033[1;31mFastflow with " << max_workers
|
||||||
|
<< " workers\033[0m" << endl;
|
||||||
|
{
|
||||||
|
vector<long long int> results;
|
||||||
|
for (int i = 0; i < average_max; ++i) {
|
||||||
|
results.push_back(fastflow({image}, neig,
|
||||||
|
iter_for_num_workers,
|
||||||
|
&gameOfLife, max_workers));
|
||||||
|
}
|
||||||
|
csvfile << std::accumulate(results.begin(), results.end(), 0) /
|
||||||
|
average_max;
|
||||||
|
}
|
||||||
|
csvfile << ",";
|
||||||
|
|
||||||
|
cout << "\033[1;31mStdthread with " << max_workers
|
||||||
|
<< " workers\033[0m" << endl;
|
||||||
|
{
|
||||||
|
vector<long long int> results;
|
||||||
|
for (int i = 0; i < average_max; ++i) {
|
||||||
|
results.push_back(stdthreads({image}, neig,
|
||||||
|
iter_for_num_workers,
|
||||||
|
&gameOfLife, max_workers));
|
||||||
|
}
|
||||||
|
csvfile << std::accumulate(results.begin(), results.end(), 0) /
|
||||||
|
average_max;
|
||||||
|
}
|
||||||
|
csvfile << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
csvfile.close();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
96
reader.hpp
Normal file
96
reader.hpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
#pragma once
|
||||||
|
#ifndef READER_HPP
|
||||||
|
#define READER_HPP
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <future>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <ff/ff.hpp>
|
||||||
|
|
||||||
|
#include "task.hpp"
|
||||||
|
|
||||||
|
using namespace ff;
|
||||||
|
|
||||||
|
template <typename T> class Reader : public ff_node_t<Task<T>> {
|
||||||
|
std::vector<std::string> Images;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Reader(std::vector<std::string> images);
|
||||||
|
Task<T> *svc(Task<T> *);
|
||||||
|
void operator()(std::vector<std::string> images,
|
||||||
|
std::vector<std::promise<Task<T> *> *> *OutputVector);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> Task<T> *read_one_image(std::string imagename);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Reader<T>::Reader(std::vector<std::string> images) : Images(images) {}
|
||||||
|
|
||||||
|
// svc function for fastflow library, function as an emitter: generates all
|
||||||
|
// tasks from the list of paths
|
||||||
|
template <typename T> Task<T> *Reader<T>::svc(Task<T> *) {
|
||||||
|
for (std::string s : Images) {
|
||||||
|
Task<T> *t = read_one_image<T>(s);
|
||||||
|
ff_node::ff_send_out(t);
|
||||||
|
}
|
||||||
|
return this->EOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// operator for std thread
|
||||||
|
template <typename T>
|
||||||
|
void Reader<T>::operator()(
|
||||||
|
std::vector<std::string> images,
|
||||||
|
std::vector<std::promise<Task<T> *> *> *OutputVector) {
|
||||||
|
|
||||||
|
assert(images.size() >= (*OutputVector).size() &&
|
||||||
|
"Error: wrong length for promise vector [Reader::operator()]");
|
||||||
|
int count = 0;
|
||||||
|
for (std::promise<Task<T> *> *output : *OutputVector) {
|
||||||
|
output->set_value(read_one_image<T>(images[count]));
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Task<T> *read_one_image(std::string image_name) {
|
||||||
|
T *image;
|
||||||
|
int32_t rows;
|
||||||
|
int32_t cols;
|
||||||
|
|
||||||
|
const std::string &filepath(image_name);
|
||||||
|
|
||||||
|
std::fstream file{filepath, file.binary | file.in};
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cerr << "Error: Failed to open " << filepath << std::endl;
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
file.read(reinterpret_cast<char *>(&rows), sizeof(rows));
|
||||||
|
file.read(reinterpret_cast<char *>(&cols), sizeof(cols));
|
||||||
|
image = new T[rows * cols];
|
||||||
|
file.read(reinterpret_cast<char *>(image), (rows * cols) * sizeof(T));
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Task<T> *task;
|
||||||
|
|
||||||
|
std::vector<std::vector<T>> *matrix =
|
||||||
|
new std::vector<std::vector<T>>(rows, std::vector<T>(cols));
|
||||||
|
|
||||||
|
for (int x = 0; x < rows; ++x) {
|
||||||
|
for (int y = 0; y < cols; ++y) {
|
||||||
|
(*matrix)[x][y] = image[x * cols + y];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task = new Task<T>(matrix, rows, cols, filepath);
|
||||||
|
delete[] image;
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* READER_HPP */
|
||||||
0
report/document.bib
Normal file
0
report/document.bib
Normal file
BIN
report/document.pdf
Normal file
BIN
report/document.pdf
Normal file
Binary file not shown.
209
report/document.tex
Normal file
209
report/document.tex
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
\documentclass[12pt, oneside]{article}
|
||||||
|
|
||||||
|
%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%
|
||||||
|
%% Load Packages %%
|
||||||
|
%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%
|
||||||
|
|
||||||
|
\usepackage[
|
||||||
|
top=2cm,
|
||||||
|
bottom=2cm,
|
||||||
|
left=2cm,
|
||||||
|
right=2cm,
|
||||||
|
headheight=20pt,
|
||||||
|
centering
|
||||||
|
]{geometry}
|
||||||
|
\geometry{a4paper}
|
||||||
|
|
||||||
|
\usepackage[utf8]{inputenc} %% use UTF-8, maybe not needed since 2018
|
||||||
|
\usepackage[italian,main=english]{babel} %% language
|
||||||
|
|
||||||
|
\pagestyle{headings}
|
||||||
|
|
||||||
|
\usepackage{scrlayer-scrpage}
|
||||||
|
\usepackage{csquotes} %% correct language also for citations
|
||||||
|
|
||||||
|
\ifoot[]{}
|
||||||
|
\cfoot[]{}
|
||||||
|
\ofoot[\pagemark]{\pagemark}
|
||||||
|
\pagestyle{scrplain}
|
||||||
|
|
||||||
|
\usepackage[
|
||||||
|
backend=biber,
|
||||||
|
style=numeric,
|
||||||
|
sorting=ynt
|
||||||
|
]{biblatex} %% for citations
|
||||||
|
\addbibresource{document.bib}
|
||||||
|
|
||||||
|
\usepackage{import} %% specify path for import
|
||||||
|
|
||||||
|
%% math packages
|
||||||
|
\usepackage{graphicx} %% for pictures
|
||||||
|
\usepackage{float}
|
||||||
|
\usepackage{amssymb} %% math symbols
|
||||||
|
\usepackage{amsmath} %% math matrix etc
|
||||||
|
\usepackage{minted} %% code block
|
||||||
|
\usepackage{tabularray} %% better tables
|
||||||
|
\usepackage{booktabs} %% rules for tables
|
||||||
|
\usepackage{mathrsfs}
|
||||||
|
\usepackage{mathtools}
|
||||||
|
\usepackage{algorithm} %% for algorithms
|
||||||
|
\usepackage{algpseudocode} %% loads algorithmicx
|
||||||
|
\usepackage{amsthm}
|
||||||
|
\usepackage{thmtools} %% theorems
|
||||||
|
|
||||||
|
%% plot packages
|
||||||
|
\usepackage{pgfplots} %% plots used with \begin{tikzpicture}
|
||||||
|
\usepackage{tikz} %% for pictures
|
||||||
|
\usetikzlibrary{trees}
|
||||||
|
\pgfplotsset{width=10cm,compat=newest}
|
||||||
|
|
||||||
|
%% design packages
|
||||||
|
\usepackage{enumitem} %% for lists and enumerating
|
||||||
|
\usepackage{color}
|
||||||
|
\usepackage{xcolor,colortbl} % xcolor for defining colors, colortbl for table colors
|
||||||
|
\usepackage{makecell} %% for multiple lines in cell of table
|
||||||
|
\usepackage{cancel}
|
||||||
|
\usepackage{pgfornament} %% ornaments
|
||||||
|
|
||||||
|
%% load last
|
||||||
|
\usepackage[hidelinks]{hyperref} %% links for table of contents, load last
|
||||||
|
\usepackage{bookmark} %% for better table of contents
|
||||||
|
|
||||||
|
|
||||||
|
%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%
|
||||||
|
%% Configuration of the packages %%
|
||||||
|
%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%
|
||||||
|
|
||||||
|
\linespread{1}
|
||||||
|
\raggedbottom %% spaces if page is empty % chktex 1
|
||||||
|
|
||||||
|
%% set max table of contents recursion to subsection (3->subsubsecition)
|
||||||
|
\setcounter{tocdepth}{3}
|
||||||
|
\setcounter{secnumdepth}{3}
|
||||||
|
|
||||||
|
%% use bar instead of arrow for vectors
|
||||||
|
\renewcommand{\vec}[1]{\bar{#1}}
|
||||||
|
%% easy norm
|
||||||
|
\newcommand{\norm}[1]{\left\lvert#1\right\rvert}
|
||||||
|
|
||||||
|
% argmin and argmax
|
||||||
|
\DeclareMathOperator*{\argmax}{argmax}
|
||||||
|
\DeclareMathOperator*{\argmin}{argmin}
|
||||||
|
|
||||||
|
%% itemize use less vertical space (use olditemize for default behaviour)
|
||||||
|
\let\olditemize=\itemize%% old itemize
|
||||||
|
\let\endolditemize=\enditemize%% old end itemize
|
||||||
|
\renewenvironment{itemize}{\olditemize\itemsep-0.2em}{\endolditemize}
|
||||||
|
|
||||||
|
%% items in itemize emph+box
|
||||||
|
%% usage: \ieb{Class:} for simple item
|
||||||
|
%% \ieb[4cm]{Class:} for specific size of box
|
||||||
|
\newcommand{\ieb}[2][2cm]{
|
||||||
|
\makebox[#1][l]{\emph{#2}}
|
||||||
|
} %% TODO: replace with description environment (? maybe)
|
||||||
|
|
||||||
|
% less vertical space around align & align*
|
||||||
|
\newcommand{\zerodisplayskips}{
|
||||||
|
\setlength{\abovedisplayskip}{0pt}
|
||||||
|
\setlength{\belowdisplayskip}{0pt}
|
||||||
|
\setlength{\abovedisplayshortskip}{0pt}
|
||||||
|
\setlength{\belowdisplayshortskip}{0pt}
|
||||||
|
}
|
||||||
|
|
||||||
|
% make dotfill use all the space available
|
||||||
|
\renewcommand{\dotfill}{
|
||||||
|
\leavevmode\cleaders\hbox to 1.00em{\hss .\hss }\hfill\kern0pt } % chktex 1 chktex 26
|
||||||
|
|
||||||
|
\setlength{\fboxsep}{-\fboxrule} % for debugging
|
||||||
|
|
||||||
|
|
||||||
|
%% PACKAGE algorithm
|
||||||
|
\floatname{algorithm}{Algorithm}
|
||||||
|
|
||||||
|
|
||||||
|
%% PACKAGE tabularray
|
||||||
|
\UseTblrLibrary{amsmath}
|
||||||
|
|
||||||
|
|
||||||
|
%% PACKAGE color
|
||||||
|
\definecolor{red}{rgb}{1, 0.1, 0.1}
|
||||||
|
\definecolor{lightgreen}{rgb}{0.55, 0.87, 0.47}
|
||||||
|
\definecolor{gray}{rgb}{0.3, 0.3, 0.3}
|
||||||
|
\newcommand{\lgt}{\cellcolor{lightgreen}} %% light green in tables
|
||||||
|
\newcommand{\gry}{\textcolor{gray}} %% gray text
|
||||||
|
\newcommand{\rd}{\textcolor{red}} %% red text
|
||||||
|
|
||||||
|
%% PACKAGE minipage
|
||||||
|
\newcommand{\thend}[1]{\begin{center}
|
||||||
|
\begin{minipage}[c][1em][c]{#1}
|
||||||
|
\dotfill{}
|
||||||
|
\end{minipage}
|
||||||
|
\end{center}}
|
||||||
|
|
||||||
|
|
||||||
|
%% PACKAGE thmtools
|
||||||
|
\declaretheoremstyle[
|
||||||
|
headfont=\normalfont\bfseries,
|
||||||
|
notefont=\mdseries,
|
||||||
|
bodyfont=\normalfont,
|
||||||
|
qed=\qedsymbol % chktex 1
|
||||||
|
]{steo}
|
||||||
|
\declaretheorem[numbered=no, style=steo]{theorem}
|
||||||
|
|
||||||
|
\declaretheoremstyle[
|
||||||
|
headfont=\normalfont\bfseries,
|
||||||
|
notefont=\mdseries,
|
||||||
|
bodyfont=\normalfont,
|
||||||
|
]{sdef}
|
||||||
|
\declaretheorem[numbered=no, style=sdef]{definition}
|
||||||
|
|
||||||
|
\declaretheoremstyle[
|
||||||
|
spaceabove=-6pt,
|
||||||
|
spacebelow=6pt,
|
||||||
|
headfont=\normalfont\bfseries,
|
||||||
|
bodyfont=\normalfont,
|
||||||
|
postheadspace=1em,
|
||||||
|
qed=$\blacksquare$,
|
||||||
|
headpunct={:}
|
||||||
|
]{sprf}
|
||||||
|
\declaretheorem[name={Proof}, style=sprf, numbered=no]{prof}
|
||||||
|
|
||||||
|
%% ......................................................................... %%
|
||||||
|
%% local changes
|
||||||
|
% \setcounter{secnumdepth}{0}
|
||||||
|
|
||||||
|
\graphicspath{ {./import/} }
|
||||||
|
|
||||||
|
%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%
|
||||||
|
|
||||||
|
\title{Document}
|
||||||
|
\author{
|
||||||
|
Elvis Rossi
|
||||||
|
}
|
||||||
|
\date{\today}
|
||||||
|
|
||||||
|
%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
|
||||||
|
\section{Implementation Design}
|
||||||
|
\subsection{Design Choices}
|
||||||
|
The class \texttt{Stencil} holds both the parallel implementation using the FastFlow library and using the native C++ threads. The one using C++ threads can be called with the method \texttt{stdthread}. The operator \texttt{()} instead will use the FastFlow library. The class can also be used as a node; an example is given in the file ``main.cpp'', where using the function \texttt{fastflow} creates a pipe between the reader, the stencil and the writer.
|
||||||
|
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.4\textwidth]{pipeline.eps}
|
||||||
|
\caption{}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
The class \texttt{Reader} reads a binary file composed of 4 bytes representing the number of rows, 4 bytes representing the number of columns and then the raw matrix data. The result is stored in the class \texttt{Task} which will be passed to the next node in the FastFlow implementation. If instead the operator \texttt{()} is called, the resulting task will be returned via the promise given as input.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\end{document}
|
||||||
|
|
||||||
|
%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%
|
||||||
|
|
||||||
|
%%% Local Variables:
|
||||||
|
%%% TeX-command-extra-options: "-shell-escape"
|
||||||
|
%%% End:
|
||||||
3259
report/import/pipeline.eps
Normal file
3259
report/import/pipeline.eps
Normal file
File diff suppressed because it is too large
Load Diff
278
stencil.hpp
Normal file
278
stencil.hpp
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/* -*- 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 * task->Cols) / 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 k = l * delta; k < (l + 1) * delta; ++k) {
|
||||||
|
int x = k / task->Cols;
|
||||||
|
int y = k % task->Cols;
|
||||||
|
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 k = (MaxThreads - 1) * delta;
|
||||||
|
k < task->Cols * task->Rows; ++k) {
|
||||||
|
int x = k / task->Cols;
|
||||||
|
int y = k % task->Cols;
|
||||||
|
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 * task->Cols,
|
||||||
|
[&](const int k) {
|
||||||
|
int x = k / task->Cols;
|
||||||
|
int y = k % task->Cols;
|
||||||
|
if (x < Borders[1] || x >= task->Rows - Borders[3] ||
|
||||||
|
y < Borders[0] || y >= task->Cols - Borders[2]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 */
|
||||||
39
task.hpp
Normal file
39
task.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
#pragma once
|
||||||
|
#ifndef TASK_HPP
|
||||||
|
#define TASK_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
template <typename T> class Task {
|
||||||
|
public:
|
||||||
|
Task(std::vector<std::vector<T>> *data, int32_t rows, int32_t cols,
|
||||||
|
std::string name);
|
||||||
|
Task(std::vector<std::vector<T>> *data, int32_t rows, int32_t cols);
|
||||||
|
~Task();
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::vector<std::vector<T>> *VectorData;
|
||||||
|
int32_t Rows, Cols;
|
||||||
|
std::filesystem::path PathName;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Task<T>::Task(std::vector<std::vector<T>> *data, int32_t rows, int32_t cols,
|
||||||
|
std::string name)
|
||||||
|
: VectorData(data), Rows(rows), Cols(cols), PathName(name) {
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Task<T>::Task(std::vector<std::vector<T>> *data, int32_t rows, int32_t cols)
|
||||||
|
: VectorData(data), Rows(rows), Cols(cols) {
|
||||||
|
PathName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> Task<T>::~Task() {
|
||||||
|
delete (VectorData);
|
||||||
|
}
|
||||||
|
#endif /* READER_HPP */
|
||||||
BIN
tests/empty2x2
Normal file
BIN
tests/empty2x2
Normal file
Binary file not shown.
BIN
tests/equation
Normal file
BIN
tests/equation
Normal file
Binary file not shown.
BIN
tests/equation2
Normal file
BIN
tests/equation2
Normal file
Binary file not shown.
BIN
tests/increasing300x200
Normal file
BIN
tests/increasing300x200
Normal file
Binary file not shown.
BIN
tests/increasing4x6
Normal file
BIN
tests/increasing4x6
Normal file
Binary file not shown.
BIN
tests/random400x2500
Normal file
BIN
tests/random400x2500
Normal file
Binary file not shown.
102
threadPool.cpp
Normal file
102
threadPool.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
#include <threadPool.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
ThreadPool::ThreadPool() {
|
||||||
|
const uint32_t ThreadNum = std::thread::hardware_concurrency();
|
||||||
|
// create all threads waiting
|
||||||
|
for (uint32_t i = 0; i < ThreadNum; ++i) {
|
||||||
|
Threads.emplace_back(std::thread(&ThreadPool::spin, this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// creates at maximum maxThreads threads, if 0 then use the maximum concurrency
|
||||||
|
// possible
|
||||||
|
ThreadPool::ThreadPool(uint32_t MaxThreads) {
|
||||||
|
const uint32_t ThreadNum =
|
||||||
|
(MaxThreads > 0)
|
||||||
|
? std::min(std::thread::hardware_concurrency(), MaxThreads)
|
||||||
|
: std::thread::hardware_concurrency();
|
||||||
|
// create all threads waiting
|
||||||
|
for (uint32_t i = 0; i < ThreadNum; ++i) {
|
||||||
|
Threads.emplace_back(std::thread(&ThreadPool::spin, this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ThreadPool::numberOfThreads() { return Threads.size(); }
|
||||||
|
|
||||||
|
void ThreadPool::spin() {
|
||||||
|
// wait for a new job on the mutex, then run the job
|
||||||
|
// comunicate that a job was finished after job is executed
|
||||||
|
while (true) {
|
||||||
|
std::function<void()> job;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(QueueMutex);
|
||||||
|
MutexCondition.wait(lock, [&] { return !Jobs.empty() || Terminate; });
|
||||||
|
if (Terminate) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++WorkingThreads;
|
||||||
|
job = Jobs.front();
|
||||||
|
Jobs.pop();
|
||||||
|
}
|
||||||
|
job();
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(QueueMutex);
|
||||||
|
--WorkingThreads;
|
||||||
|
if (WorkingThreads == 0) {
|
||||||
|
JobCondition.notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadPool::addJob(const std::function<void()> &job) {
|
||||||
|
if (Terminated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
{ // add one job, sync with mutex
|
||||||
|
std::unique_lock<std::mutex> lock(QueueMutex);
|
||||||
|
Jobs.push(job);
|
||||||
|
}
|
||||||
|
// notify that there is one job
|
||||||
|
MutexCondition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ThreadPool::waitEnd() {
|
||||||
|
// waits on the condition until there are no more jobs and all threads are
|
||||||
|
// idle, should return false
|
||||||
|
if (Terminated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(QueueMutex);
|
||||||
|
JobCondition.wait(lock,
|
||||||
|
[&] { return Jobs.empty() && WorkingThreads == 0; });
|
||||||
|
}
|
||||||
|
return !Jobs.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadPool::Stop() {
|
||||||
|
// joins all threads (lets them finish the current job)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(QueueMutex);
|
||||||
|
Terminate = true;
|
||||||
|
}
|
||||||
|
MutexCondition.notify_all();
|
||||||
|
for (std::thread &t : Threads) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
Threads.clear();
|
||||||
|
Terminated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadPool::~ThreadPool() {
|
||||||
|
if (!Terminated) {
|
||||||
|
this->Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
35
threadPool.hpp
Normal file
35
threadPool.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
#pragma once
|
||||||
|
#ifndef THREADPOOL_HPP
|
||||||
|
#define THREADPOOL_HPP
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <functional>
|
||||||
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class ThreadPool {
|
||||||
|
public:
|
||||||
|
ThreadPool();
|
||||||
|
explicit ThreadPool(uint32_t MaxThreads);
|
||||||
|
void addJob(const std::function<void()> &Job);
|
||||||
|
int numberOfThreads();
|
||||||
|
void Stop();
|
||||||
|
bool waitEnd();
|
||||||
|
~ThreadPool();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void spin();
|
||||||
|
|
||||||
|
bool Terminate = false;
|
||||||
|
bool Terminated = false;
|
||||||
|
std::atomic_int WorkingThreads = 0;
|
||||||
|
std::mutex QueueMutex;
|
||||||
|
std::condition_variable MutexCondition;
|
||||||
|
std::condition_variable JobCondition;
|
||||||
|
std::vector<std::thread> Threads;
|
||||||
|
std::queue<std::function<void()>> Jobs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* THREADPOOL_HPP */
|
||||||
27
timer.cpp
Normal file
27
timer.cpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "timer.hpp"
|
||||||
|
|
||||||
|
UTimer::UTimer() : TimeStart(std::chrono::high_resolution_clock::now()) {
|
||||||
|
TimeElapsed = std::chrono::high_resolution_clock::now() - TimeStart;
|
||||||
|
Timeusec =
|
||||||
|
std::chrono::duration_cast<std::chrono::microseconds>(TimeElapsed)
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UTimer::start() { TimeStart = std::chrono::high_resolution_clock::now(); }
|
||||||
|
|
||||||
|
void UTimer::stop() {
|
||||||
|
TimeElapsed = std::chrono::high_resolution_clock::now() - TimeStart;
|
||||||
|
Timeusec =
|
||||||
|
std::chrono::duration_cast<std::chrono::microseconds>(TimeElapsed)
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
long long int UTimer::print(const std::string &s) {
|
||||||
|
std::cout << s << "\t" << Timeusec << " usecs" << std::endl;
|
||||||
|
return Timeusec;
|
||||||
|
}
|
||||||
22
timer.hpp
Normal file
22
timer.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
#pragma once
|
||||||
|
#ifndef TIMER_HPP
|
||||||
|
#define TIMER_HPP
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class UTimer {
|
||||||
|
public:
|
||||||
|
UTimer();
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
long long int print(const std::string &s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::chrono::time_point<std::chrono::high_resolution_clock> TimeStart;
|
||||||
|
std::chrono::duration<double> TimeElapsed;
|
||||||
|
long long int Timeusec;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* TIMER_HPP */
|
||||||
76
writer.hpp
Normal file
76
writer.hpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
#pragma once
|
||||||
|
#ifndef WRITER_HPP
|
||||||
|
#define WRITER_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <future>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <ff/ff.hpp>
|
||||||
|
|
||||||
|
#include "task.hpp"
|
||||||
|
|
||||||
|
using namespace ff;
|
||||||
|
|
||||||
|
template <typename T> class Writer : public ff::ff_node_t<Task<T>, void> {
|
||||||
|
public:
|
||||||
|
void *svc(Task<T> *);
|
||||||
|
void operator()(std::vector<std::shared_future<Task<T> *>> *);
|
||||||
|
};
|
||||||
|
|
||||||
|
// svc function for fastflow library
|
||||||
|
template <typename T> void *Writer<T>::svc(Task<T> *in) {
|
||||||
|
write_one_image(in);
|
||||||
|
return this->GO_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
// operator for std thread
|
||||||
|
template <typename T>
|
||||||
|
void Writer<T>::operator()(
|
||||||
|
std::vector<std::shared_future<Task<T> *>> *ResultsVector) {
|
||||||
|
if (!ResultsVector) {
|
||||||
|
std::cerr << "Error: invalid pointer to vector of futures "
|
||||||
|
"[Writer::operator()]"
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto Result : *ResultsVector) {
|
||||||
|
Task<T> *t = Result.get();
|
||||||
|
write_one_image(t);
|
||||||
|
delete (t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes out just one image, prepend the output image with "out_"
|
||||||
|
template <typename T> void write_one_image(Task<T> *t) {
|
||||||
|
std::string Pre = "out_";
|
||||||
|
std::filesystem::path Path = t->PathName;
|
||||||
|
std::string NameNew = Pre + Path.filename().string();
|
||||||
|
Path.replace_filename(NameNew);
|
||||||
|
|
||||||
|
std::fstream File{Path, File.out | File.binary};
|
||||||
|
if (!File.is_open()) {
|
||||||
|
std::cerr << "Error: Failed to open " << t->PathName.string() << std::endl;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
File.write(reinterpret_cast<char *>(&t->Rows), sizeof(t->Rows));
|
||||||
|
File.write(reinterpret_cast<char *>(&t->Cols), sizeof(t->Cols));
|
||||||
|
|
||||||
|
// use temporary buffer to not call the write function too frequently
|
||||||
|
T *buffer = new T[t->Cols];
|
||||||
|
for (int x = 0; x < t->Rows; ++x) {
|
||||||
|
for (int y = 0; y < t->Cols; ++y) {
|
||||||
|
buffer[y] = (*t->VectorData)[x][y];
|
||||||
|
}
|
||||||
|
File.write(reinterpret_cast<char *>(buffer), t->Cols * sizeof(T));
|
||||||
|
}
|
||||||
|
delete[] buffer;
|
||||||
|
File.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* WRITER_HPP */
|
||||||
Reference in New Issue
Block a user