- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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: "<<:THIS_THREAD::GET_ID>
std::cout< return NULL;
}
};
int main(array<:STRING> ^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.
Link Copied
17 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
>>...
As soon as a local scope is left a destructor of the'task_scheduler_init' classwill be called and it will destroy
the scheduler.
...
>>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<:STRING> ^args )
{
{ // these make a difference // <=local scope START
tbb::task_scheduler_init init(6);
} // <=local scope END
...
Best regards,
Sergey
...
Best regards,
Sergey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
"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.
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
(Edited 2012-03-19) Funny typo apparently corrected.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
#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.
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
That's right, unless you explicitly call terminate() somewhere.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
#14 "Actually, there is difference."
Well, you're describing exactly what I meant, so no... :-)
Well, you're describing exactly what I meant, so no... :-)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Oh, it seems I missed just one word: you told about multiple successive instances. Then, yes, I agree, no difference :) Sorry for confusion.
Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page