2

In my program there is lot of loops that can be easily rewritten into multithread. Basically for each function which should be multithreadd I am writing following function:

void func_to_threaded(int i_from, int i_to, int num_th, ...other_parameters)
{
    int i_min = i_from;
    int i_max = i_to;   
    int i_inc = i_max / num_th;
    i_max = i_max % num_th + i_inc;
    thread* th_dens = new thread[num_th];
    for (int i = 0; i < num_th; i++)
    {
        th_dens[i] = thread(func_one_thread, i_min, i_max, ...other_parameters);
        i_min = i_max;
        i_max += i_inc;
    }
    for (int i = 0; i < num_th; i++) th_dens[i].join();
    delete[] th_dens;
}

Is there a way to rewrite this to be generic for every function of form

void func_one_thread(int i_min, int i_max, ...other_parameters)
Michal
  • 671
  • 3
  • 9
  • 22
  • Just gonna say, that function doesn't look like it splits the work evenly, unless `i_from` is always 0. Tried mentally going through it with `(3, 8, 2, ...)`, and ended up with `thread(func_one_thread, 3, 4, ...)` and `thread(func_one_thread, 4, 8, ...)`. So, unless I missed something, it may be a bit uneven. – Justin Time - Reinstate Monica Dec 02 '16 at 23:15
  • Yes, in my code `i_from` is always zero and now I just tried to rewrite it more generally but failed... – Michal Dec 03 '16 at 07:20
  • Ah. Not too familiar with multithreading myself, but maybe `i_inc = (i_max - i_min) / num_th;` would work. You'd need to check if `i_max - i_min` was cleanly divisible by `num_th`, though, to see if any of the threads need to do extra work. – Justin Time - Reinstate Monica Dec 03 '16 at 20:44
  • Yes, I corrected this (see answer below). If `i_max - i_min` is not divisible by `num_thread` then the first thread is doing extra work - achieved by mod `num_thread` (`%`). – Michal Dec 03 '16 at 20:51
  • I would suggest you look at OpenMP which will do it very well for you according to the number of cores you have at run-time. – Mark Setchell Dec 04 '16 at 09:37
  • https://computing.llnl.gov/tutorials/openMP/ – Mark Setchell Dec 04 '16 at 11:33
  • At first glance, OpenMP seems extra easy to implement. Thanks! – Michal Dec 06 '16 at 08:34

3 Answers3

1

I'm not going to answer your question with a template, altough it's certainly a valid approach. I'm going to reccoment insted to use thread pool, and wrapping your all actions into a common interface. see eg.: 1, 2, with boost: 3

'stay high level'

Community
  • 1
  • 1
Piotr Falkowski
  • 1,957
  • 2
  • 16
  • 24
1

Based on Piotr Falkowski's suggestion I used threadpool with boost library to write this class

// header file
#include "threadpool.hpp"
class c_Pool
{
public:
    // CONSTRUCTORS
    c_Pool(int num_thread);

    // VARIABLES
    int num_thread;
    boost::threadpool::pool th_pool;

    // METHODS
    void add_task(int i_from, int i_to, std::function<void(int, int)> func);
};

// main file
c_Pool::c_Pool(int num_thread):
    num_thread(num_thread), th_pool(num_thread)
{}

void c_Pool::add_task(int i_from, int i_to, function<void(int, int)> func)
{
    int i_min = i_from;
    int i_max = i_to; 
    int i_inc = (i_max - i_min) / num_thread;
    i_max = i_from + i_inc // initial i_max
          + (i_max - i_min) % num_thread; // first thread is doing extra work

    for (int i = 0; i < num_thread; i++)
    {
        auto func_one_thread = bind(func, i_min, i_max);        
        th_pool.schedule(func_one_thread);
        i_min = i_max;
        i_max += i_inc;
    }
    th_pool.wait();
}

and every function void some_func(int i_min, int i_max, ...other_parameters) I`m multithreading with

auto tmp_func = bind(some_func, placeholders::_1, placeholders::_2, ...other_parameters);
pool.add_task(i_from, i_to, tmp_func);

EDIT corrected line wth setting initial i_max

Community
  • 1
  • 1
Michal
  • 671
  • 3
  • 9
  • 22
  • 1
    I'm not sure, but shouldn't the 4th line of `add_task()` be `i_max = i_min + ((i_max - i_min) % num_thread + i_inc);`? It's currently possible for `i_max` to be the same as, or less than, `i_min` after that line. (For example, if `i_from` is `8`, `i_to` is `16`, and `num_thread` is `2`, then... `i_min` is set to `8`, `i_max` is set to `16`, `i_inc` is set to `4` (`(16 - 8) / 2` -> `8 / 2` -> `4`), and `i_max` is set to `4` (`(16 - 8) % 2 + 4` -> `8 % 2 + 4` -> `0 + 4` -> `4`).) Apart from that, though, the math looks good, as far as I can tell. – Justin Time - Reinstate Monica Dec 03 '16 at 23:24
  • Yes, you are right. In my program I'm using `i_from = 0` so I never encountered this error... – Michal Dec 04 '16 at 08:53
  • If something looks a bit suspect, it can be useful to try running through it with unusual inputs, to see if it works properly. – Justin Time - Reinstate Monica Dec 04 '16 at 18:03
0

It`s been a while since I asked this question and I moved a while back from boost threadpool to much more elegant and simple OpenMP as originally suggested Mark Setchell. So my code now looks very simple

omp_set_num_threads(num_thread);

#pragma omp parallel for private(private_params)
for(int i = i_min; i < i_max; i++){
    some_func(parameters);
}
Michal
  • 671
  • 3
  • 9
  • 22