76
C++ Templates and Higher Level Synthesis or “How much can we over-engineer an accumulator” ?

Or “How much can we over-engineer an accumulator” ?

Embed Size (px)

Citation preview

  • Slide 1
  • or How much can we over-engineer an accumulator ?
  • Slide 2
  • What are we trying to achieve? Code reuse Supporting multiple number formats Functionality customisation
  • Slide 3
  • What are templates?
  • Slide 4
  • Defer specification of types
  • Slide 5
  • Introducing the Accumulator class accum { private: int value_; public: accum() : value_(0) {} void add(int n) { value_ += n; } int result() { return value_; } };
  • Slide 6
  • Templating the Accumulator template class accum { private: int value_; public: accum() : value_(0) {} void add(int n) { value_ += n; } int result() { return value_; } }; Add template declaration Replace int with template parameter
  • Slide 7
  • Templating the Accumulator template class accum { private: Inc value_; public: accum() : value_(0) {} void add(Inc n) { value_ += n; } Inc result() { return value_; } }; Add template declaration Replace int with template parameter Generalise code that relied on int
  • Slide 8
  • Templating the Accumulator template class accum { private: Inc value_; public: accum() : value_() {} void add(Inc n) { value_ += n; } Inc result() { return value_; } }; Add template declaration Generalise code that relied on int Replace int with template parameter
  • Slide 9
  • Using the Accumulator accum acc1; accum > acc2; accum acc3; Same as original code complex is part of the standard library We can even use it to concatenate strings
  • Slide 10
  • What are templates Defer specification of types Allowed types determined by required expressions
  • Slide 11
  • What are the expressions? template class accum { private: Inc value_; public: accum() : value_() {} void add(Inc n) { value_ += n; } Inc result() { return value_; } }; Has a += operator Default construction Can be copied The required set of operations is a Concept
  • Slide 12
  • Concepts Entirely determined by valid expressions Generally exist only in documentation The accum template class takes one argument that must be Incrementable An Incremental class is default and copy constructable and have the following valid expressions where i1 and i2 are instances of Incrementable: i1 += i2// Increment operator The accum template class takes one argument that must be Incrementable An Incremental class is default and copy constructable and have the following valid expressions where i1 and i2 are instances of Incrementable: i1 += i2// Increment operator
  • Slide 13
  • Concepts accum acc1; accum > acc2; accum acc3; Has built in += operator Have overloaded += operators
  • Slide 14
  • What happens if you fail? The compiler will error The compiler has no knowledge of the concept The error message could be pretty messy
  • Slide 15
  • What happens if you fail? accum acc1; accum > acc2; acc2.add(a_list); Compiles fine Templates are only evaluated if used error: no match for operator+= in ((accum >*)this)->accum >::value_ += n error: no match for operator+= in ((accum >*)this)->accum >::value_ += n This doesnt refer to anything on this slide
  • Slide 16
  • What are templates Defer specification of types Allowed types determined by required expressions Evaluated at compile time
  • Slide 17
  • Compilation Preprocessor Compilation Code Generation Synthesis Templates evaluated here HDL generated here template class accum {}; accum a; accum b; template class accum {}; accum a; accum b; class accum_int {}; class accum_double {}; accum_int a; accum_double b; class accum_int {}; class accum_double {}; accum_int a; accum_double b;
  • Slide 18
  • What are templates? Defer specification of types Allowed types determined by required expressions Evaluated at compile time Operate on semantics rather than syntax
  • Slide 19
  • Why not Macros? Macros Templates #define MIN(x,y) x accum_mult; Non type parameters Default parameters Initialise value_ with parameter Initialise value_ with parameter Addition typedef doesnt change as default value can be used Addition typedef doesnt change as default value can be used
  • Slide 38
  • Bringing it all together template class accum { int value_; public: accum() : value_(Initial) {} void add(int n) { value_ = Func()(value_, n); } int result() { return value_; } }; Reintroduce Inc template parameter Reintroduce Inc template parameter
  • Slide 39
  • Bringing it all together template class accum { Inc value_; public: accum() : value_(Initial) {} void add(Inc n) { value_ = Func()(value_, n); } Inc result() { return value_; } }; Reintroduce Inc template parameter Reintroduce Inc template parameter Make addition the default
  • Slide 40
  • Bringing it all together template, int Initial = 0> class accum { Inc value_; public: accum() : value_(Initial) {} void add(Inc n) { value_ = Func()(value_, n); } Inc result() { return value_; } }; Reintroduce Inc template parameter Reintroduce Inc template parameter Make addition the default
  • Slide 41
  • Bringing it all together template, int Initial = 0> class accum { Inc value_; public: accum() : value_(Initial) {} void add(Inc n) { value_ = Func()(value_, n); } Inc result() { return value_; } }; What happens if T cannot be constructed from an int? What happens if T cannot be constructed from an int? What about the maximum for a float/double? What about the maximum for a float/double?
  • Slide 42
  • Someone Elses Problem template, int Initial = 0> class accum { Inc value_; public: accum() : value_(Initial) {} void add(Inc n) { value_ = Func()(value_, n); } Inc result() { return value_; } }; Make the user provide a suitable initial value Make the user provide a suitable initial value
  • Slide 43
  • Someone Elses Problem template > class accum { Inc value_; public: accum(Inc v = Inc()) : value_(v) {} void add(Inc n) { value_ = Func()(value_, n); } Inc result() { return value_; } }; Make the user provide a suitable initial value Make the user provide a suitable initial value
  • Slide 44
  • Everyone Elses Problem template class ALU { Acc1 acc1_; Acc2 acc2_; public: ALU(): acc1_(), acc2_() {} }; We have to let users pass the correct defaults What should go here?
  • Slide 45
  • Factory Fun template > class accum { Inc value_; public: accum(Inc v = Inc()) : value_(v) {} void add(Inc n) { value_ = Func()(value_, n); } Inc result() { return value_; } }; Define a simple factory
  • Slide 46
  • Factory Fun template struct default_factory { T create() { return T(); } }; template > class accum { Inc value_; public: accum(Inc v = Inc()) : value_(v) {} void add(Inc n) { value_ = Func()(value_, n); } Inc result() { return value_; } }; Define a simple factory Add the factory as a template argument
  • Slide 47
  • Factory Fun template struct default_factory { T create() { return T(); } }; template, typename Fact = default_factory > class accum { Inc value_; public: accum(Inc v = Inc()) : value_(v) {} void add(Inc n) { value_ = Func()(value_, n); } Inc result() { return value_; } }; Define a simple factory Add the factory as a template argument Use the factory to initialise value_
  • Slide 48
  • Factory Fun template struct default_factory { T create() { return T(); } }; template, typename Fact = default_factory > class accum { Inc value_; public: accum(Fact f = Fact()) : value_(f.create()) {} void add(Inc n) { value_ = Func()(value_, n); } Inc result() { return value_; } }; Define a simple factory Add the factory as a template argument Use the factory to initialise value_
  • Slide 49
  • Factory Fun template struct mult_factory { T create() { return T(1); } }; typedef accum, mult_factory > int_mult; Our multiplication accum requires a new factory to give the correct initial value Our multiplication accum requires a new factory to give the correct initial value We can then typedef away the horribleness
  • Slide 50
  • What about Max/Min? template struct max_factory { T create() { return /* max value of T */;} }; Need a template that does different things depending on type Need a template that does different things depending on type
  • Slide 51
  • Specialisation template struct max_factory; template struct max_factory { int create() { return INT_MAX; } }; template struct max_factory { double create() { return DBL_MAX; } } We cant do anything here so leave it undefined We cant do anything here so leave it undefined Give the compiler a version which works for ints One of these required for every type We probably ought to make this possible to reuse
  • Slide 52
  • Traits template struct num_limits {}; template struct num_limts { static int max() { return INT_MAX; } static int min() { return INT_MIN; } } template struct max_factory { T create() { return num_limits ::max(); } } Create an undefined class Specialise for what we care about Specialise for what we care about Use the trait class to implement the factory
  • Slide 53
  • Built-in traits #include template struct max_factory { T create() { return std::numeric_limits ::max(); } }; template struct min_factory { T create() { return std::numeric_limits ::min(); } }; Why reinvent the wheel?
  • Slide 54
  • (Hopefully) Final - Factories template struct default_factory { T create() { return T(); } }; template struct mult_factory { T create() { return T(1); } }; template struct max_factory { T create() { return std::numeric_limits ::max(); } }; template struct min_factory { T create() { return std::numeric_limits ::min(); } };
  • Slide 55
  • (Hopefully) Final - Accumulator template, typename Fact = default_factory > class accum { Inc value_; public: accum(Fact f = Fact()) : value_(f.create()) {} void add(Inc n) { value_ = Func()(value_, n); } Inc result() { return value_; } };
  • Slide 56
  • (Hopefully) Final - Client Code typedef accum string_append; typedef accum, mult_factory > double_mult; typedef accum, max_factory > int_and; We could make this nicer with MORE traits but we have to stop somewhere. We could make this nicer with MORE traits but we have to stop somewhere.
  • Slide 57
  • Compile time dimensional analysis
  • Slide 58
  • Consider a simple unit system Three base units meter for length second for time kilogram for mass All derived units have a scaling factor of one Only integer powers
  • Slide 59
  • Define our quantity template struct quantity { double value; quantity(double value):value(value) {} }; typedef quantity meter; typedef quantity second; typedef quantity kilogram; Length, time and mass are template parameters Length, time and mass are template parameters Constructor to make creating quantities easier Constructor to make creating quantities easier Construction can be implicit from a double
  • Slide 60
  • Addition template quantity operator+ (quantity lhs, quantity rhs) { return lhs.value + rhs.value; } The two units have to be the same Add the values Function can take any quantity type Function can take any quantity type
  • Slide 61
  • Multiplication template quantity operator*(quantity lhs, quantity rhs) { return lhs.value * rhs.value; } The two quantities may be different types Return type sums the powers of the base units Return type sums the powers of the base units The only code executed at runtime is the multiplication Division is similar
  • Slide 62
  • Using our system typedef quantity speed; typedef quantity acceleration; speed s = meters(2) / seconds(1); acceleration a = s / seconds(1); speed s2 = meters(2) * seconds(1); Compile time error
  • Slide 63
  • Benefits All dimensional analysis is compile time only No runtime overhead No effect on synthesisability All this is provided by Boost in a more general way Preprocessor issues prevent synthesis (for now)
  • Slide 64
  • Well some of you asked
  • Slide 65
  • What is it? We can get the compiler to perform computation Discovered rather than invented A (sort of) real world use of functional programming
  • Slide 66
  • Hello World! (-ish) template struct factorial { static const int value = ???; }; Can we make value equal to the factorial of I ? Can we make value equal to the factorial of I ? No loops, but we can recurse
  • Slide 67
  • Hello World! (-ish) template struct factorial { static const int value = I * factorial ::value; }; template struct factorial { static const int value = 1; } We just copy the mathematical recursive definition We just copy the mathematical recursive definition Specialisation to implement the base case Specialisation to implement the base case
  • Slide 68
  • Use cases? Static loop unrolling
  • Slide 69
  • Loop Unrolling int accumulate(int value) { static accum acc; acc.add(value); return acc.result(); } Higher level synthesis wrapper functions need to hold state statically Higher level synthesis wrapper functions need to hold state statically What happens if we want to compose wrapper functions?
  • Slide 70
  • Loop Unrolling int accumulate(int value) { static accum acc; acc.add(value); return acc.result(); } int accum_wrapper(int value) { if (value % 2 == 0) return accumulate(value); else return accumulate(value); } Wed like to accumulate odd and even numbers separately. Wed like to accumulate odd and even numbers separately. Both accumulate function calls will use the same static data
  • Slide 71
  • Loop Unrolling template int accumulate(int value) { static accum acc; acc.add(value); return acc.result(); } int accum_wrapper(int value) { if (value % 2 == 0) return accumulate(value); else return accumulate(value); } Make the function a template Use different template parameters for each instance Use different template parameters for each instance No effect on functionality, just allows us to create independent instances No effect on functionality, just allows us to create independent instances
  • Slide 72
  • Loop Unrolling template int accumulate(int value) { static accum acc; acc.add(value); return acc.result(); } int accum_wrapper(int value) { if (value % 2 == 0) return accumulate (value); else return accumulate (value); } Make the function a template Use different template parameters for each instance Use different template parameters for each instance No effect on functionality, just allows us to create independent instances No effect on functionality, just allows us to create independent instances What if we want to control the number of instances?
  • Slide 73
  • Loop Unrolling template struct looper { static int loop(int value, int key) { if (key == I) return accumulate (value); else return looper ::loop(value, key); } } template struct looper { static int loop(int value, int key) { return accumulate (value); } } key is the runtime provided index Recursive call to next index Base case to avoid infinite recursion
  • Slide 74
  • Loop Unrolling int accum_wrapper(int value) { return looper ::loop(value, value % 2); } int accum_wrapper(int value) { return looper ::loop(value, value % 16); } This is the maximum index rather than the loop iterator count This is the maximum index rather than the loop iterator count Easily extendable to 16 (or more) accumulators
  • Slide 75
  • Use Cases? Static loop unrolling Type conditional compilation Calculation of bus widths/ranges Other things I havent thought of
  • Slide 76
  • Questions? Comments?