Image Component Library (ICL)
|
Utility class for parallelizing algorithms. More...
#include <MultiThreader.h>
Classes | |
class | Work |
plugin class for work packages performed parallel More... | |
Public Types | |
typedef std::vector< Work * > | WorkSet |
set of work packages, that should be performed parallel | |
Public Member Functions | |
MultiThreader () | |
Empty (null) constructor. | |
MultiThreader (int nThreads) | |
Default constructor with defined set of working threads. | |
void | operator() (WorkSet &ws) |
applying operator (performs each Work* element of ws parallel) | |
int | getNumThreads () const |
returns the number of WorkThreads |
Utility class for parallelizing algorithms.
The Multithreader class provides a simple interface to parallelize algorithms. To achieve a multi-threaded implementation of an algorithm, you have to split the computation loop into N parts (Work-Packages or short Work). Each of this Works will be computed in a single thread internally when given to the apply operator of the MultiThreader.
Please note This tool was written before openmp became popular and part of compilers. Today we recommend to use openmp rather then the Multithreader class.
The following example explains how to parallelize a simple function-call
#include <ICLQt/Quick.h> #include <cmath> #include <ICLUtils/MultiThreader.h> // a simple function calculating the l2 norm on a data array void l2norm_vec(float *dataStart,float *dataEnd, int dimPerElem, float *dst){ for(;dataStart<dataEnd;dst++){ float accu = 0; for(int i=0;i<dimPerElem;i++,dataStart++){ accu += (*dataStart) * (*dataStart); } *dst = sqrt(accu); } } // Wrapper for this function (implementing the MultiThreader::Work interface) struct L2NormWork : public MultiThreader::Work{ L2NormWork(float *dataStart,float *dataEnd, int dimPerElem, float *dst): dataStart(dataStart),dataEnd(dataEnd),dimPerElem(dimPerElem),dst(dst){ } float *dataStart,*dataEnd; int dimPerElem; float *dst; // interface function -> calls the wrapped function virtual void perform(){ l2norm_vec(dataStart,dataEnd,dimPerElem,dst); } }; int main(){ // creating some really BIG data array const int DIM = 10000000; // dimension of each data element int dim = 100; float *data = new float[DIM]; float *dst = new float[DIM/dim]; // apply single threaded 100 times: tic(); for(int i=0;i<100;i++){ l2norm_vec(data,data+DIM,dim,dst); } toc(); //--- multi threaded part -------------------------- // create a MultiThreader with 2 Threads MultiThreader mt(2); // create a WorkSet with 2 work packages MultiThreader::WorkSet ws(2); // 1st package: first half of the array ws[0] = new L2NormWork(data,data+DIM/2,dim,dst); // 2nd package: second half of the array ws[1] = new L2NormWork(data+DIM/2,data+DIM,dim,dst+DIM/2); // apply 100 times multi-threaded in 2 Threads tic(); for(int i=0;i<100;i++){ mt(ws); } toc(); // release the work instances delete ws[0]; delete ws[1]; delete [] data; delete [] dst; return 0; }
The following benchmark results were obtained:
However, the MultiThreader provides a powerful interface for parallelizing code, it is still a bit inconvenient to use. The Example above has shown, that the programmer has to write about 10 additional line for the function wrapper and another 4 lines to create the WorkSet and to fill the MultiThreader instance with it.
For more convenience some additional high level classes and functions should be implemented. For instance the SplittedUnaryop class of the ICLFilter package, which provides a top level interface for parallelizing unary operators (class interface UnaryOp)
typedef std::vector<Work*> icl::utils::MultiThreader::WorkSet |
set of work packages, that should be performed parallel
Empty (null) constructor.
icl::utils::MultiThreader::MultiThreader | ( | int | nThreads | ) |
Default constructor with defined set of working threads.
int icl::utils::MultiThreader::getNumThreads | ( | ) | const |
returns the number of WorkThreads
void icl::utils::MultiThreader::operator() | ( | WorkSet & | ws | ) |
applying operator (performs each Work* element of ws parallel)