1 / 38

Would you like some syntactic sugar with your TBB?

Would you like some syntactic sugar with your TBB?. Evan Driscoll. What is TBB?. C++ library for writing concurrent programs Classes for: Loop-based concurrency Task-based concurrency Pipelines Concurrent data structures Atomic types …. TBB good?. I’m excited

ethan
Download Presentation

Would you like some syntactic sugar with your TBB?

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Would you like some syntactic sugar with your TBB? Evan Driscoll

  2. What is TBB? • C++ library for writing concurrent programs • Classes for: • Loop-based concurrency • Task-based concurrency • Pipelines • Concurrent data structures • Atomic types • …

  3. TBB good? • I’m excited • Integrates “well” with C++ • Large library, many paradigms • Broader than MapReduce, OpenMP

  4. TBB syntax • Low-level syntax • templates, operator overloading, placement new • High-level syntax • Tons of boilerplate code • main.C in parallel_while othello went from 206 to 424 lines (doing 3 loops), >58 boilerplate • Separation of code • Loop bodies lexically moved

  5. TBB syntax while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); /* Recurse */ quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  6. while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); /* Recurse */ quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } } parallel_while<LookaheadMoveEvaluator> w; QueueGenerator g(moves); LookaheadMoveEvaluator e(nBest, board, other_color, newdepth); w.run(g, e);

  7. class QueueGenerator { queue<OthelloMove> & _moves; public: QueueGenerator (queue<OthelloMove> & moves) : _moves(moves) { } bool pop_if_present (OthelloMove & out_move) { if(_moves.empty()) return false; else { out_move = _moves.front(); _moves.pop(); return true; } } }; while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); /* Recurse */ quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  8. class LookaheadMoveEvaluator { atomic<int> & _nBest; OthelloBoard const & _board; char _otherColor; int _newDepth; public: LookaheadMoveEvaluator (atomic<int> & nBest, OthelloBoard const & board, char otherColor, int newDepth) : _nBest(nBest) , _board(board) , _otherColor(otherColor) , _newDepth(newDepth) {} while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); /* Recurse */ quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  9. void operator() (OthelloMove & move) const { OthelloBoard b = _board; b.applyMove(move); int quality = Lookahead( b, _otherColor, _newDepth ); int curNBest = _nBest; while(quality > curNBest && _nBest.compare_and_swap (quality, curNBest) != curNBest) { curNBest = _nBest; } } typedef OthelloMove argument_type; }; while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); /* Recurse */ quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  10. But these transformations are all mechanical!

  11. class QueueGenerator { queue<OthelloMove> & _moves; public: QueueGenerator (queue<OthelloMove> & moves) : _moves(moves) { } bool pop_if_present (OthelloMove & out_move) { if(_moves.empty()) return false; else { out_move = _moves.front(); _moves.pop(); return true; } } }; while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); /* Recurse */ quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  12. class LookaheadMoveEvaluator { atomic<int> & _nBest; OthelloBoard const & _board; char _otherColor; int _newDepth; public: LookaheadMoveEvaluator (atomic<int> & nBest, OthelloBoard const & board, char otherColor, int newDepth) : _nBest(nBest) , _board(board) , _otherColor(otherColor) , _newDepth(newDepth) {} while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); /* Recurse */ quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  13. class LookaheadMoveEvaluator { atomic<int> & _nBest; OthelloBoard const & _board; char _otherColor; int _newDepth; public: LookaheadMoveEvaluator (atomic<int> & nBest, OthelloBoard const & board, char otherColor, int newDepth) : _nBest(nBest) , _board(board) , _otherColor(otherColor) , _newDepth(newDepth) {} while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); /* Recurse */ quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  14. othello.cc othello.o tbbetter gcc So do this automagically • Extend the syntax of C++ • Implement a source-to-source transformation

  15. othello.cc othello.o tbbetter gcc parsing type checking translation pretty printing So do this automagically

  16. New syntax • tbb_shared: new qualifier • Concept: variable is shared between threads • Implementation: a reference is used in the class

  17. New syntax • concurrent_for(var , start , end [, grainsize]) • var can name an existing variable or declare a new one; must be compatible with blocked_range • Iterates over [ start,end ) • If omitted grainsize, uses auto_partitioner() • Can’t specify different increments • Current status: parses (and typechecks), but transformation incomplete

  18. New syntax • cwhile_iterator type itervar;concurrent_while(cond) { cwhile_generator {genstmts }bodystmts} • Generates two classes • Generator (stream) class checks cond; if true, runs genstmts • Body class runs bodystmts • Communicate via a cwhile_iterator • genstmts should write to itervar • bodystmts should read from itervar

  19. New syntax • cwhile_iterator type itervar;concurrent_while(cond) { cwhile_generator {genstmts }bodystmts} • Current status: implemented • Only minor restrictions: no nested concurrent_while, types of variables used restricted

  20. C++ elision • #define: • concurrent_while -> while • cwhile_iterator -> • tbb_shared -> • concurrent_for(var, start, end, …) -> for(var=start, var != end, ++var) • But doesn’t work if var declares a variable

  21. Othello revisited concurrent_while( moves.size() > 0 ) { cwhile_generator { move = moves.front(); moves.pop(); } OthelloBoard b = board; b.applyMove(move); int quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { tbb::spin_mutex::scoped_lock l ((tbb::spin_mutex&)lock); if(quality > nBest) { nBest = quality; } } } while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); /* Recurse */ quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  22. What changed? while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  23. What changed? while( moves.size() ) { b = board; b.applyMove( moves.front() ); moves.pop(); quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  24. What changed? while( moves.size() ) { b = board; OthelloMove move = moves.front(); b.applyMove(move); moves.pop(); quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  25. What changed? while( moves.size() ) { b = board; OthelloMove move = moves.front(); b.applyMove(move); moves.pop(); quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  26. What changed? while( moves.size() ) { OthelloMove move = moves.front(); moves.pop(); b = board; b.applyMove(move); quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { nBest = quality; } }

  27. What changed? while( moves.size() ) { OthelloMove move = moves.front(); moves.pop(); OthelloBoard b = board; b.applyMove(move); int quality = Lookahead( b, other_color, newdepth ); if(quality > nBest) { nBest = quality; } }

  28. What changed? concurrent_while( moves.size() ) { cwhile_generator { OthelloMove move = moves.front(); moves.pop(); } OthelloBoardb = board; b.applyMove(move); intquality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { tbb::spin_mutex::scoped_lock l((tbb::spin_mutex&)lock); if(quality > nBest) { nBest = quality; } } }

  29. Done! concurrent_while( moves.size() ) { cwhile_generator { OthelloMove move = moves.front(); moves.pop(); } OthelloBoard b = board; b.applyMove(move); int quality = Lookahead( b, other_color, newdepth ); if( quality > nBest ) { tbb::spin_mutex::scoped_lock l((tbb::spin_mutex&)lock); if(quality > nBest) { nBest = quality; } } }

  30. Anything else? • #includes for TBB headers • Removed declaration for OthelloBoard b and int quality • Added cwhile_iterator to definition of move • Added tbb_shared to definition of nBest • Added a declaration for the lock • Added another declaration of OthelloBoard b in another branch • Created a task_scheduler_init object in main

  31. Anything else? • Changed CC=g++ and LD=g++ to use tbbetter driver • Added a –I and –L flag to the TBB directories • Added –ltbb to the linker flags

  32. Interesting point: constness • Loop-carried dependencies wrong • This translates to not modifying thread-local variables • Any not marked tbb_shared! • Which iteration did the value come from? • Also applies to reading the value of a variable changed in the loop • TBB enforces: operator() is const

  33. Performance (othello, lookahead 7)

  34. Wrapping up • Got some done… more to do • Goals if this were a longer-term project (or if I hadn’t procrastinated like mad): • Finish concurrent_for • Find a translation for parallel_reduce • Make cilk code compile • Goals for a serious project • Engineering on the parser: • Bug squashing (still 7 or so outstanding; 5 “patched” by sed) • Integrate with other compilers • Improve edge cases • “If you can do 80% of the job with 50% of the work, that is the right way”

  35. Questions?

  36. Alternate syntax • Alternate names: • cwhile_generator?! • Really easy to change • Updating test cases probably longer process • tbb_shared implies volatile? • Seems useful, but already broke

  37. Alternate syntax • concurrent_while(iterator, cond) { } • Explicit as to what iterator is being used • Currently: you must use exactly one variable declared with cwhile_iterator inside the body of each concurrent_while • concurrent_while(…) { cwhile_generator { … } cwhile_body { … }}

  38. What really happens othello.cc cc1plus -E modified elsa sed othello.o cc1plus -E cc1plus as

More Related