5

Note: this is NOT a duplicate of this quesiton.

Given a complex software parallelized with TBB, how do I completely switch off threading? I'm aware of the task_scheduler_init:

int nthreads = tbb::task_scheduler_init::default_num_threads();
const char* cnthreads = getenv("TBB_NUM_THREADS");
if (cnthreads) nthreads = std::max(1, atoi(cnthreads));

tbb::task_arena arena(nthreads, 1);
tbb::task_scheduler_init init(nthreads);

However, this solution (related to this) does not switch off threading. TBB still creates lots of threads, nthreads just makes some of them not being used. Moreover, if one has nthreads = 1, TBB actually creates 1 extra thread - totaling 2 threads together with master thread.

Yes, there are certain situations when you'd want to really switch off threading completely, yet keeping the TBB code alive. My current solution is a sloppy wrapper around tbb:

namespace hddm {
bool enableTBB = true;
class task_group {
    unique_ptr<tbb::task_group> tbb;
public :
    task_group() {
        if (enableTBB)
            tbb.reset(new tbb::task_group());
    }

    template<typename F>
    void run(const F& f) {
        if (tbb)
            tbb->run(f);
        else
            f();
    }

    void wait() {
        if (tbb)
            tbb->wait();
    }
};

class task_arena {
    unique_ptr<tbb::task_arena> tbb;
public :
    task_arena(int nthreads, int dedicated) {
        if (enableTBB)
            tbb.reset(new tbb::task_arena(nthreads, dedicated));
    }

    template<typename F>
    void execute(const F& f) {
        if (tbb)
            tbb->execute(f);
    }
};

class task_scheduler_init {
    unique_ptr<tbb::task_scheduler_init> tbb;
public :
    task_scheduler_init(int nthreads) {
        if (enableTBB)
            tbb.reset(new tbb::task_scheduler_init(nthreads));
    }
};

class parallel_for {
public :
    template<typename F>
    parallel_for(const tbb::blocked_range<int>& r, const F& f) {
        if (enableTBB)
            tbb::parallel_for(r, f);
        else
            f(r);
    }
};

} // namespace hddm

I hope TBB/Intel experts can recommend a better solution, thank you!

Dmitry Mikushin
  • 1,478
  • 15
  • 16

2 Answers2

4

Using tbb::task_scheduler_init has been deprecated since Intel TBB 4.3 Update 5 (see Documentation and this forum thread). You can now modify this behavior using the tbb::global_control class on a global scope.

B--rian
  • 5,578
  • 10
  • 38
  • 89
Fischmiep
  • 41
  • 3
  • unfortunately Intel's resources are full of nonsense. How come `tbb::global_control` example itself uses "deprecated" `task_scheduler_init`? The old forum thread you refer to actually was the source of my original sample code. But as stated in the question, it creates threads nonetheless. – Dmitry Mikushin Jan 25 '20 at 17:42
  • 1
    Please see a complete test case here: https://github.com/dmikushin/serial_tbb Note when `tbb::global_control` is uncommented, it looks like indeed a single thread is used, but is assigned to "master" and is doing nothing. As a result, there are 0 worker threads, and the application just deadlocks. – Dmitry Mikushin Jan 25 '20 at 18:02
4

For posterity,

#include <tbb/global_control.h>

// somewhere
tbb::global_control c(tbb::global_control::max_allowed_parallelism, 1);

https://spec.oneapi.com/versions/0.5.0/oneTBB/task_scheduler/tbb_global_control.html

scx
  • 3,221
  • 1
  • 19
  • 37
  • 1
    Do you do this at global scope? And did you test it? The querent's comment on another answer says they tried something like that (https://github.com/dmikushin/serial_tbb/blob/a18be52eac3bf8a18f9e57d47d1c96c47d62c479/src/main.cpp#L32), but got 1 master thread an 0 workers, resulting in deadlock. – Peter Cordes May 28 '21 at 19:25
  • 1
    From the docs : "The impact of the control variable ends when its lifetime is complete." I use it all the time for debugging. – scx May 29 '21 at 22:30