- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
currently my main loop looks something like this (very simplified)
int main()
{
while( /*Runnning*/)
{
/* Win32 Message Handling */
tbb::task_list taskList;
taskList.push_back(renderer_->CreateTask());
taskList.push_back(ai_->CreateTask());
taskList.push_back(animation_->CreateTask());
taskList.push_back(physics_->CreateTask());
tbb::task::spawn_root_and_wait(taskList);
/* synchronize shared memory */
}
}
basicly when the Message ahndling ahs finished each subsystem creates a "task"
which is then executed...
the issues here is that the loop has to wait for the "taskList" to finish executing...
which i dont want... i would need some way to check if the "taskList" has finished...
and only then respawn...
so i can do this
int main()
{
tbb::task_list taskList;
while( /*Runnning*/)
{
/* Win32 Message Handling */
if(/* tasklist finished */)
{
taskList.push_back(renderer_->CreateTask());
taskList.push_back(ai_->CreateTask());
taskList.push_back(animation_->CreateTask());
taskList.push_back(physics_->CreateTask());
tbb::task::spawn(taskList);
/* synchronize shared memory */
}
}
}
any suggestions?
alternativly i could have each task "respawn itself" once its finished... and id only do the "wait_for_all" when the application stops...
but im unsure how i would do that... i could have the task keep adding children to itself... but then how would i stop the execution?
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
nagy:
ive just converted my 3d engine from boost::threads to tbb and the task system... which have greatly improved hte performacne of my application... however i have one issue where i do not want the "main windows message handling" thread to sleep...
I think that the 'right way' to do this is to detect when both tasks have completed and send a message to main thread via PostMessage().
How detect when both tasks have completed you can see here:
http://software.intel.com/en-us/forums//topic/60503
Basically, you need some reference counter, which initially initialized to the number of tasks. When task completes it decrements counter. When counter hits zero, you send a message to main thread.
Here is very dirty code sketch:
main_loop()
{
int ref_count;
for (;;)
{
int msg = GetMessage();
if (msg == SPAWN_CHILD_TASKS) {
ref_count = 2;
spawn(new child_task(&ref_count));
spawn(new child_task(&ref_count));
// not waiting for them to complete, just spawn
}
else if (other messages)
....
}
}
void child_task(int* ref_count)
{
// do real work
if (0 == InterlockedDecrement(ref_count))
// it's the last task, notify main thread
PostMessage(main_thread, SPAWN_CHILD_TASKS);
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
class System
{
void Run()
{
isRunning_ = true;
/* do stuff */
isRunning_ = false;
}
atomic
}
and my main loop looks like this now
tbb::task* execute()
{
MSG msg = {0};
while (msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
if(!demo_->renderer_->IsRunning() && !demo_->simulation_->IsRunning())
{
demo_->camera_->Update();
demo_->factory_->Synchronize();
tbb::task& a = *new( allocate_child() ) Renderer::Task(demo_->renderer_.get());
tbb::task& b = *new( allocate_child() ) Simulation::Task(demo_->simulation_.get());
set_ref_count(2);
spawn(a);
spawn(b);
}
}
}
demo_->ExitCode(msg.wParam);
return NULL;
}
however i get a debug assertion from tbb that says that "attempt to spawn task that is not in "allocated" state"
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
nagy:another question i have... will the "parent" task(thread) be continously running or will it "time slice" with its children? wouldnt be to good if it took an entire worker thread just to loop the messages constantly...
My opinion is that is such situation it's better to not use TBB's worker thread to run message loop at all.
I.e. you run message loop on your own thread (main thread or additionally created thread). That thread submits work to TBB. TBB tasks notify main thread by sending messages via PostMessage, or by setting some variables.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
And of course either execute() should never be made to wait, much less to busy-wait (disclaimer: I only briefly glanced at the code), although it is always possible to "oversubscribe" by one if this thread is in fact waiting almost all of the time (use task_scheduler_init and default_num_threads).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
nagy,
The following code should work for your message dispatch loop:
tbb::task* execute()
{
MSG msg = {0};
set_ref_count(1);
while (msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
if(!demo_->renderer_->IsRunning() && !demo_->simulation_->IsRunning())
{
demo_->camera_->Update();
demo_->factory_->Synchronize();
tbb::task& a = *new( allocate_additional_child_of(*this) )
Renderer::Task(demo_->renderer_.get());
tbb::task& b = *new( allocate_additional_child_of(*this) )
Simulation::Task(demo_->simulation_.get());
spawn(a);
spawn(b);
}
}
} // message dispatch loop
wait_for_all();
demo_->ExitCode(msg.wParam);
return NULL;
}
To fix your code, I first of all replaced allocate_child with allocate_additional_child_of, which atomically increments the reference counter of the parent. Then, I added another reference with set_ref_count(1) at the very beginning; this reference is required to call wait_for_all later.
The problem with this solution is exactly as you supposed: the thread will spin in the while loop; it will not participate in the processing of spawned tasks, which means concurrency is mandated, i.e. there should be at least one more thread to process the tasks. What's worse, such busy spinning takes resources away from worker threads.
For a GUI application that should also do some extensive computation, the usual solution is to separate computations into another thread and keep GUI responsive in the main thread. To minimize resource consumption, messages are obtained with GetMessage which makes the caller to sleep if there is no messages, thus yielding compute power to the worker thread(s). The notification that is complete can (or even should?) be done by putting a message into the queue. The notification to start the new portion of work can be done via an event.
Now let's extend this design to use TBB. The main thread still should serve GUI and provide the work to other threads. The problem as we see is to get response that the work is completed - the main thread had to poll on some flags (IsRunning in your case), thus it can't sleep waiting for a message anymore.But if the previous solution for communicationwith a single worker thread worked well, why don't keep it? Let's make that worker thread responsible for further sharing of work, and use TBB from there, not from the GUI thread.
That's the main idea (which is the same actually as Dmitry suggested earlier). You can start this working thread either way convenient to you; TBB suggests the class tbb_thread for that purpose. That thread should wait for a signal from the main thread, then spawn tasks, and wait_for_all of them, and repeat. I believe you will figure out the rest of details, or possibly already did.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
ive implemented this using GetMessage, PostThreadMessage and atomic
now my problem here is how do i create tbb "child" tasks in a thread that is not a tbb "root" task...
// Main thread loop
if(msg.message == SPAWN_CHILD_TASKS )
{
/* spawn tbb tasks ??? */
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
the only way to spawn a tbb task here is to use "spawn_root_and_wait" which is not what i want... it shouldnt "wait"... and i cant put this loop in a "root task" since "GetMessage" must be in the same thread as the window was created...
EDIT:
"That's the main idea (which is the same actually as Dmitry suggested earlier). You can start this working thread either way convenient to you; TBB suggests the class tbb_thread for that purpose. That thread should wait for a signal from the main thread, then spawn tasks, and wait_for_all of them, and repeat. I believe you will figure out the rest of details, or possibly already did."
i missed this part....
i got it now... thx...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Well, if you still want the main thread to be involved in task spawning... Here is what you could do:
- before entering the message dispatch loop, create tbb::task_scheduler_init to start worker threads; then allocate tbb::empty_task as a root, but do not spawn it, only set the reference count:
tbb::task& e = *new (tbb::task::allocate_root()) tbb::empty_task;
e.set_ref_count(1); - inside the loop, use this fake root task to allocate and spawn the child tasks, following the rules I outlined in my previous post:
e.spawn( *new (e.allocate_additional_child_of(e)) MyTaskClass()); - after the loop, call wait_for_all on the root task to wait for remaining children, and after it returns, call the method destroy for the roottask passing itself as the parameter:
e.wait_for_all();
e.destroy(e);
(Edit) But to me, the solution with a separate thread controlling all the TBB stuff is preferrable. And I just saw your edit above :)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
im getting 0.05s long spikes where no thread is active... the main thread is sleeping and the two tbb worker threads are "spinning"...
this didnt happen when i simply had the main thread spawning tasks (the reason this was bad was because the "main thread had to wait for the tasks to finish")...
i know its hard to tell just like that... but does someone have an idea might be causing this?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
nagy:
im getting 0.05s long spikes where no thread is active... the main thread is sleeping and the two tbb worker threads are "spinning"...
i know its hard to tell just like that... but does someone have an idea might be causing this?
There is a subtle bug... okay, not bug, feature:
http://software.intel.com/en-us/forums//topic/58670
which can cause dormancy... because you don't have enough 'parallel slack' :)
But I don't think that it can be steadily reproducible...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
ill be back
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
i dont know what caused it... but after a full rebuild of all my source it seems to work well..
1% no active thread
97% fully utilized (with no unnecessary processing)
its actually almost precisily twice as fast as when i was running single threaded and about 40% faster than when i was using boost::threads... i take that as a success...
i thank you all for the help... and Intel for such an awesome library
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm glad you now have the design that satisfies your needs. And thank you for the good words about TBB - it is always good to know that your work helped someone else :)
We were once asked about a list of projects or products that use TBB. If you (or your company) don't mind this kind of publicity, you might leave some words and/or a link to your project in that thread: http://software.intel.com/en-us/forums//topic/59636. Thanks!
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page