curious behavior of task_scheduler_init

curious behavior of task_scheduler_init

dulantha_f的头像

I have noticed that if I put the call to task_scheduler_init between curly braces the call has no effect. To demonstrate here's my code:
#include "stdafx.h" #include #include #include #include #include #include #include "tbb/compat/thread" using namespace System; //Task class:- class TaskClass : public tbb::task { public: TaskClass(){}; ~TaskClass(){}; tbb::task* execute() { std::stringstream ss; // to keep the output in order ss<<"Thread id: "< std::cout<

return NULL; } };

int main(array ^args) { { // these make a difference tbb::task_scheduler_init init(6); } TaskClass& task1 = *new(tbb::task::allocate_root()) TaskClass(); tbb::task::enqueue(task1,tbb::priority_normal); TaskClass& task2 = *new(tbb::task::allocate_root()) TaskClass(); tbb::task::enqueue(task2,tbb::priority_normal); TaskClass& task3 = *new(tbb::task::allocate_root()) TaskClass(); tbb::task::enqueue(task3,tbb::priority_normal); TaskClass& task4 = *new(tbb::task::allocate_root()) TaskClass(); tbb::task::enqueue(task4,tbb::priority_normal); TaskClass& task5 = *new(tbb::task::allocate_root()) TaskClass(); tbb::task::enqueue(task5,tbb::priority_normal); TaskClass& task6 = *new(tbb::task::allocate_root()) TaskClass(); tbb::task::enqueue(task6,tbb::priority_normal); std::string temp; std::cin>>temp; // letting other threads execute return 0; } With braces the output: Thread id: 6640 Thread id: 6640 Thread id: 6640 Thread id: 6640 Thread id: 6640 Thread id: 6640 Without braces the output: Thread id: 6884 Thread id: 6200 Thread id: 7020 Thread id: 5984 Thread id: 6420 Thread id: 6884 From documentation I understand that with 3.x each thread from application gets its own pool. But in the above example it is the same thread but seems like with curly braces the pool doesn't get created with the set number. I'm using TBB version 4.0 update 1. Is this the intended behavior or a bug? Thank you.

18 帖子 / 0 new
最新文章
如需更全面地了解编译器优化,请参阅优化注意事项
Sergey Kostrov的头像

>>...
>>Is this the intended behavior or a bug?

As soon as a local scope is left a destructor of the'task_scheduler_init' classwill be called and it will destroy
the scheduler.

...
int main( array ^args ) { { // these make a difference // <=local scope START tbb::task_scheduler_init init(6); } // <=local scope END
...

Best regards,
Sergey

Raf Schietekat的头像

"As soon as a local scope is left a destructor of the'task_scheduler_init' classwill be called and it will destroy the scheduler."
Please note that task_scheduler_init is merely a reference to the scheduler, so the scheduler is created when the first reference appears and only destroyed when the last reference in the entire program goes away. A reference can also be implicit, if the thread does anything TBB-related without an existing explicit task_scheduler_init. But other than that the explanation is correct.

dulantha_f的头像

So if I need an if statement for example, if (poolSize>0) task_scheduler_init init(poolSize); won't cut it. I need to use a pointer and do a new on task_schedule_init? Is this a correct approach?

Terry Wilmarth (Intel)的头像

You could create the task_scheduler_init object before the if, using the "deferred" initialization option. Then you can call "initialize" with poolSize inside the if. See 12.2.1 in the reference about deferred, and 12.2.3 for initialize.

Raf Schietekat的头像

No, a task_scheduler_init instance has an internal reference to the shared scheduler data structures. If you don't want to rely on implicit scheduler initialisation, you should allocate a task_scheduler_init instance on the stack, i.e., as an "automatic" varable, so that its lifetime encompasses the thread's use of TBB facilities (make sure that there's a long-lasting instance in the main thread to avoid overhead from tear-down and recreation of the scheduler). I wouldn't try to allocate it on the heap, even if it does work. The code shown, if it compiles at all (maybe it needs a block?), will allocate a task_scheduler_init instance and immediately destroy it, so it doesn't do anything other than waste some time.

dulantha_f的头像

Let me give some background to my setup. I need a separate class to do TBB tasks, cannot be done in main. class TBBTasks { public: TBBTasks(){} ~TBBTasks(){ delete _init; } setThreadPoolSize(int size) { // following lines are what I have now and so far seems to work _poolSize=size;
if (_poolSize>0) _init = new tbb::task_scheduler_init((SCAInt32)_poolSize); } //users use this method to queue tasks to TBB scheduler void queue(TaskClass class) // This is a dummy task class. I have a separate class that inherits from //tbb::task. { tbb::task::enqueue([instance of my separate class],priority); } private: int _poolSize; tbb::task_scheduler_init* _init;// current setup though maybe unappropriate }; So using a similar (any changes to the methods are welcome) class how can I initialize the scheduler with a set thread pool size so that whenever users queue tasks the scheduler runs with the same number of threads?

Raf Schietekat的头像

Allocation on the heap will probably work, but it requires more care, as illustrated by the missing initialisation of _init, and by what can go wrong if setThreadPoolSize() is called multiple times, all assuming that the task_scheduler_init instance is created before any other TBB activity happens in the thread and that the instance is only accessed from one thread. You can also allocate a mutex' scoped_lock on the heap, but that similarly defeats the design. Also, you should watch your spelling of variable names. :-)

(Edited 2012-03-19) Funny typo apparently corrected.

Alexey Kukanov (Intel)的头像

Creating the instance of task_scheduler_init with deferred initialization, and calling initialize() to it when appropriate, as Terry suggested, should work fine for this case.

dulantha_f的头像

But the problem is my queue() method is called at different various times. So the scheduler will have to be initialized in the queue method everytime then. And it will be brought down as soon as the queue() method is done. So the scheduler is initialized and destructed over and over. Isn't this a major performance hit?

Alexey Kukanov (Intel)的头像

All I meant is that in your code dynamic allocation of task_scheduler_init can be replaced with deferred initialization of a class member of this type.

Lazy initialization schemas are also possible, with the overhead as big as checking a flag in each invocation of the queue() method. You can find examples at http://stackoverflow.com/questions/9344739/thread-safe-lazy-creation-with-tbb.

dulantha_f的头像

I made a static variable and initialized it with deffered and then called initialize(size) with the pool size in the setThreadPoolSize() method. So far it seems to be working fine and I'm not doing a new. static tbb::task_scheduler_init tbb_TaskSched(task_scheduler_init::deferred); // line in the implementation file tbb_TaskSched.initialize(size); // line in the setThreadPoolSize() I feel you guys are really discouraging me to do a new. Any particular reasons as to why not do it?

Raf Schietekat的头像

#10 "But the problem is my queue() method is called at different various times. So the scheduler will have to be initialized in the queue method everytime then. And it will be brought down as soon as the queue() method is done. So the scheduler is initialized and destructed over and over. Isn't this a major performance hit?"
Indeed, but one which you can counter by keeping another thread/task_scheduler_init combination alive for the duration of the program even if it's not doing anything, e.g., by doing that in main() and then launching another thread from there.

#12 "static tbb::task_scheduler_init tbb_TaskSched(task_scheduler_init::deferred); // line in the implementation file
tbb_TaskSched.initialize(size); // line in the setThreadPoolSize()
I feel you guys are really discouraging me to do a new. Any particular reasons as to why not do it?"
I guess we're both just encouraging you to keep it simple if the scope is logically related to a position on the stack anyway, if only to avoid user error. Initially in this forum thread it wasn't even clear that you needed deferred initialisation (I'll now just accept that you do), so I didn't address the issue before, but note that there's no performance difference between having multiple successive task_scheduler_init instances (whether on the stack or on the heap) and multiple successive initialize()/terminate() uses of a single instance, so the advice above still stands.

Alexey Kukanov (Intel)的头像
Quoting Raf Schietekat ...note that there's no performance difference between having multiple successive task_scheduler_init instances (whether on the stack or on the heap) and multiple successive initialize()/terminate() uses of a single instance...

Updated: I agree. When writing the original reply, I missed that you talk about multiple successive, and not coexisting,instances.
-----
Calling terminate() on a single instance of task_scheduler_init (TSI)will deactivate it and cause thread pool destruction; subsequent initialize() will create a new pool of threads. Essentially, initialize()/terminate() do the same as construction/destruction of an instance, just at different time. And by the way, an extraTSI instance that is created as deferred does not keep a reference to the pool of threads; you need to have an active instance for that.

dulantha_f的头像
Quoting Alexey Kukanov (Intel)

And by the way, an extraTSI instance that is created as deferred does not keep a reference to the pool of threads; you need to have an active instance for that.

So in my case, once I call initialize() and since the variable is static I get a reference to the TSI during the whole run of the application right?

Alexey Kukanov (Intel)的头像

That's right, unless you explicitly call terminate() somewhere.

Raf Schietekat的头像

#14 "Actually, there is difference."
Well, you're describing exactly what I meant, so no... :-)

Alexey Kukanov (Intel)的头像

Oh, it seems I missed just one word: you told about multiple successive instances. Then, yes, I agree, no difference :) Sorry for confusion.

登陆并发表评论。