/* ---------------------------------------------------------------------------- * GTSAM Copyright 2010, Georgia Tech Research Corporation, * Atlanta, Georgia 30332-0415 * All Rights Reserved * Authors: Frank Dellaert, et al. (see THANKS for the full author list) * See LICENSE for the license information * -------------------------------------------------------------------------- */ /** * @file parallelTraversalTasks.h * @author Richard Roberts * @date April 9, 2013 */ #pragma once #include #include #include #ifdef GTSAM_USE_TBB # include # include # undef max // TBB seems to include windows.h and we don't want these macros # undef min # undef ERROR namespace gtsam { /** Internal functions used for traversing trees */ namespace treeTraversal { namespace internal { /* ************************************************************************* */ template class PostOrderTask : public tbb::task { public: const boost::shared_ptr& treeNode; boost::shared_ptr myData; VISITOR_POST& visitorPost; PostOrderTask(const boost::shared_ptr& treeNode, const boost::shared_ptr& myData, VISITOR_POST& visitorPost) : treeNode(treeNode), myData(myData), visitorPost(visitorPost) {} tbb::task* execute() { // Run the post-order visitor (void) visitorPost(treeNode, *myData); return NULL; } }; /* ************************************************************************* */ template class PreOrderTask : public tbb::task { public: const boost::shared_ptr& treeNode; boost::shared_ptr myData; VISITOR_PRE& visitorPre; VISITOR_POST& visitorPost; int problemSizeThreshold; bool makeNewTasks; bool isPostOrderPhase; PreOrderTask(const boost::shared_ptr& treeNode, const boost::shared_ptr& myData, VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost, int problemSizeThreshold, bool makeNewTasks = true) : treeNode(treeNode), myData(myData), visitorPre(visitorPre), visitorPost(visitorPost), problemSizeThreshold(problemSizeThreshold), makeNewTasks(makeNewTasks), isPostOrderPhase(false) {} tbb::task* execute() { if(isPostOrderPhase) { // Run the post-order visitor since this task was recycled to run the post-order visitor (void) visitorPost(treeNode, *myData); return NULL; } else { if(makeNewTasks) { if(!treeNode->children.empty()) { // Allocate post-order task as a continuation isPostOrderPhase = true; recycle_as_continuation(); //PostOrderTask& postOrderTask = // *new(allocate_continuation()) PostOrderTask(treeNode, myData, visitorPost); bool overThreshold = (treeNode->problemSize() >= problemSizeThreshold); tbb::task* firstChild = 0; tbb::task_list childTasks; for(const boost::shared_ptr& child: treeNode->children) { // Process child in a subtask. Important: Run visitorPre before calling // allocate_child so that if visitorPre throws an exception, we will not have // allocated an extra child, this causes a TBB error. boost::shared_ptr childData = boost::allocate_shared(tbb::scalable_allocator(), visitorPre(child, *myData)); //childTasks.push_back(*new(postOrderTask.allocate_child()) // PreOrderTask(child, childData, visitorPre, visitorPost, // problemSizeThreshold, overThreshold)); tbb::task* childTask = new(allocate_child()) PreOrderTask(child, childData, visitorPre, visitorPost, problemSizeThreshold, overThreshold); if(firstChild) childTasks.push_back(*childTask); else firstChild = childTask; } // If we have child tasks, start subtasks and wait for them to complete //postOrderTask.set_ref_count((int) treeNode->children.size()); set_ref_count((int)treeNode->children.size()); spawn(childTasks); return firstChild; } else { // Run the post-order visitor in this task if we have no children (void) visitorPost(treeNode, *myData); return NULL; } } else { // Process this node and its children in this task processNodeRecursively(treeNode, *myData); return NULL; } } } void processNodeRecursively(const boost::shared_ptr& node, DATA& myData) { for(const boost::shared_ptr& child: node->children) { DATA childData = visitorPre(child, myData); processNodeRecursively(child, childData); } // Run the post-order visitor (void) visitorPost(node, myData); } }; /* ************************************************************************* */ template class RootTask : public tbb::task { public: const ROOTS& roots; DATA& myData; VISITOR_PRE& visitorPre; VISITOR_POST& visitorPost; int problemSizeThreshold; RootTask(const ROOTS& roots, DATA& myData, VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost, int problemSizeThreshold) : roots(roots), myData(myData), visitorPre(visitorPre), visitorPost(visitorPost), problemSizeThreshold(problemSizeThreshold) {} tbb::task* execute() { typedef PreOrderTask PreOrderTask; // Create data and tasks for our children tbb::task_list tasks; for(const boost::shared_ptr& root: roots) { boost::shared_ptr rootData = boost::allocate_shared(tbb::scalable_allocator(), visitorPre(root, myData)); tasks.push_back(*new(allocate_child()) PreOrderTask(root, rootData, visitorPre, visitorPost, problemSizeThreshold)); } // Set TBB ref count set_ref_count(1 + (int) roots.size()); // Spawn tasks spawn_and_wait_for_all(tasks); // Return NULL return NULL; } }; template RootTask& CreateRootTask(const ROOTS& roots, DATA& rootData, VISITOR_PRE& visitorPre, VISITOR_POST& visitorPost, int problemSizeThreshold) { typedef RootTask RootTask; return *new(tbb::task::allocate_root()) RootTask(roots, rootData, visitorPre, visitorPost, problemSizeThreshold); } } } } #endif