- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm writing a benchmark making use of tbb tasks spawned largely with the "allocate_additional_child_of(...)" command, due to not knowing how many tasks to spawn until runtime. Currently I performan set_ref_count(1), then in a loop perform as many allocates and spawns as necessary, then doing a wait_for_all. This seems to run fine for small numbers of tasks, but if I start getting into larger values (thousands), I begin to get random assertion failures, the most common of which is "another thread emptied the return_list."
Due to getting different assertion failures for the same command line arguments, I assumed it was a memory corruption issue, and have tried tracing it down with both microsoft's debug heap and OSX's guardmalloc, both of which haven't found anything (on the contrary, the benchmark runs without problem when using malloc debug tools, possibly due to the slowdown removing any race conditions).
Is there any information on what this return_list is? The assertion fails when I try to spawn another task using allocate_additional_child_of(*this), but I'm not sure what it means. Other assertions that pop up tend to do with "small_task_count corrupted" and "attempt to spawn task that is not in allocated state", even though I only spawn tasks immediately after allocating them. Perhaps how I'm spawning tasks isn't correct? Hopefully understanding this return_list may help me trace down the issue.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
static volatile double g_vol = 0;
class leaf_task : public tbb::task
{
tbb::task* execute () {
++g_tasks_started;
for ( size_t i = 0; i < 10000; ++i )
g_vol += i/g_vol;
return NULL;
}
}; // class leaf_task
class simple_root_task : public tbb::task
{
const size_t m_task_count;
const size_t m_task_load;
tbb::task* execute () {
set_ref_count(1);
for ( size_t i = 0; i < m_task_count; ++i ) {
spawn( *new( allocate_additional_child_of(*this) ) leaf_task );
}
wait_for_all();
return NULL;
}
public:
simple_root_task ( size_t tasks, size_t taskLoad )
: m_task_count(tasks)
, m_task_load(taskLoad)
{}
}; // class simple_root_task
void Test ()
{
for ( size_t i=100; i<=1000000; i*=10 ) {
for ( size_t l=10; l<=1000000000/i; l*=10 ) {
tbb::task &r = *new( tbb::task::allocate_root() ) simple_root_task(i, l);
try {
tbb::task::spawn_root_and_wait;
} catch ( ... ) {
printf ("TestX: exception caught ");
}
printf ("Done: num tasks %d, task load %d ", i, l);
}
}
} // Test
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Here is the fixed piece of code:
class leaf_task : public tbb::task
{
tbb::task* execute () {
++g_tasks_started;
for ( size_t i = 0; i < 10000; ++i )
g_vol += i/g_vol;
return NULL;
}
public:
static tbb::task* create ( tbb::task *owner, tbb::task *parent ) {
return new( owner->allocate_additional_child_of(*parent) ) leaf_task;
}
}; // class leaf_task
class task_launcher : public tbb::task {
tbb::task* execute(){
spawn( *leaf_task::create(this, parent()) );
return NULL;
}
}; // class task_launcher
Important changes are in bold.
Note also that I turned the instance method "creator" into the static one. It allows you to avoid creating fake local objects to call the instance method, and makes the code cleaner. My heart nearly stopped when I saw that leaf_task object allocated on the stack.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
To "nzea": Don't call allocate_additional_child_of() on "parent", because "this" may have been stolen and then there are ownership issues, see reference below.
To TBB: I see "8.3.2.4 new( this.task::allocate_additional_child_of( parent ))" in reference manual revision 1.9 (may not be most recent, but I have to run now), which should be either "this->" or maybe something like "t.".
(Hey, we had a race on the thread and Andrey won... First and third paragraphs are still relevant, though.)
- 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
This discussion inspired me to upgrade my implementation of task_group on http://softwareblogs.intel.com/2008/07/02/implementing-task_group-interface-in-tbbto allow multiple threads to create tasks within the same task_group. I used task::self() for the "owner". It depends upon an undocumented implementation feature that when there is no running task ona master thread, nonetheless task::self() returns a reference to a dummy task owned by that thread.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page