Presentation is loading. Please wait.

Presentation is loading. Please wait.

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

Similar presentations


Presentation on theme: "Or “How much can we over-engineer an accumulator” ?"— Presentation transcript:

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

2 What are we trying to achieve? Code reuse Supporting multiple number formats Functionality customisation

3 What are templates?

4 Defer specification of types

5 Introducing the Accumulator class accum { private: int value_; public: accum() : value_(0) {} void add(int n) { value_ += n; } int result() { return value_; } };

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

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

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

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

10 What are templates Defer specification of types Allowed types determined by required expressions

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

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

13 Concepts accum acc1; accum > acc2; accum acc3; Has built in += operator Have overloaded += operators

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

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 doesn’t refer to anything on this slide

16 What are templates Defer specification of types Allowed types determined by required expressions Evaluated at compile time

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;

18 What are templates? Defer specification of types Allowed types determined by required expressions Evaluated at compile time Operate on semantics rather than syntax

19 Why not Macros? Macros Templates #define MIN(x,y) x { "@context": "http://schema.org", "@type": "ImageObject", "contentUrl": "http://images.slideplayer.com/14/4268796/slides/slide_19.jpg", "name": "Why not Macros.", "description": "Macros Templates #define MIN(x,y) x

20 Why not Macros? Macros Templates #define MIN(x,y) \ ((x)<(y)?(x):(y)) int a = MIN(x++,y++); int a = ((x++)<(y++)?(x++):(y++); template T min(T lhs, T rhs) { return lhs < rhs? lhs : rhs; } int a = min(x++, y++); Something will be incremented twice Something will be incremented twice We can template functions too! Type of T automatically deduced

21 Why not Macros? Macros Templates #define MIN(x,y) \ ((x)<(y)?(x):(y)) int a = MIN(x++,y++); int a = ((x++)<(y++)?(x++):(y++); template T min(T lhs, T rhs) { return lhs < rhs? lhs : rhs; } int a = min(x++, y++); Looks like a function call but isn’t Looks like a function call but isn’t Is a function call and behaves as such Is a function call and behaves as such We can template functions too!

22 Templates for functionality

23 What is a policy? Split the functionality of a class into orthogonal parts

24 Back to the Accumulator class accum { private: int value_; public: accum() : value_(0) {} void add(int n) { value_ += n; } int result() { return value_; } }; Do we always want to accumulate by adding? Do we always want to accumulate by adding?

25 Inheritance to the Rescue? class accum_base { virtual int func(int a, int b) = 0; int value_; public: accum_base() : value_() {} int next(int v) { value_ = func(value_, v); return value_; } }; class accum_add : public accum_base { int func(int a, int b) { return a + b; } }; Other classes can override func to provide other operations Other classes can override func to provide other operations Define a base class Virtual function for accumulate operation

26 Inheritance to the Rescue? Only provides one axis of specialisation Indirection in calling the function Runtime overhead Not synthesisable Also applies to function pointers

27 Back to the Accumulator class accum { int value_; public: accum() : value_(0) {} void add(int n) { value_ += n; } int result() { return value_; } }; asdf Rewrite the operation in terms of +

28 Back to the Accumulator struct int_add { int func(int a, int b) { return a + b; } }; template class accum { int value_; public: accum() : value_(0) {} void add(int n) { value_ = value_ + n; } int result() { return value_; } }; asdf Provide a class to perform the operation Template the accumulator for function Rewrite the operation in terms of + Create the function object and call

29 Back to the Accumulator struct int_add { int func(int a, int b) { return a + b; } }; template class accum { int value_; public: accum() : value_(0) {} void add(int n) { value_ = Func().func(value_, n); } int result() { return value_; } }; typedef accum accum_add; Provide a class to perform the operation Template the accumulator for function Create the function object and call A typedef can recreate the original A typedef can recreate the original

30 Functors Classes that overload the bracket operator Can be called as if functions

31 Back to the Accumulator struct int_add { int func(int a, int b) { return a + b; } }; template class accum { int value_; public: accum() : value_(0) {} void add(int n) { value_ = Func().func(value_, n); } int result() { return value_; } }; typedef accum accum_add;

32 Back to the Accumulator struct int_add { int operator()(int a, int b) { return a + b; } }; template class accum { int value_; public: accum() : value_(0) {} void add(int n) { value_ = Func()(value_, n); } int result() { return value_; } }; typedef accum accum_add; This already exists the standard library This already exists the standard library

33 #include template class accum { int value_; public: accum() : value_(0) {} void add(int n) { value_ = Func()(value_, n); } int result() { return value_; } }; typedef accum > accum_add; Back to the Accumulator Side note: beware consecutive angle brackets Side note: beware consecutive angle brackets

34 What about multiplication? How can we define non-zero initial values? How linked are the initial values and operations being performed?

35 Can we fix multiplication? template class accum { int value_; public: accum() : value_(0) {} void add(int n) { value_ = Func()(value_, n); } int result() { return value_; } };

36 Can we fix multiplication? template class accum { int value_; public: accum() : value_(0) {} void add(int n) { value_ = Func()(value_, n); } int result() { return value_; } }; Non type parameters Default parameters

37 Can we fix multiplication? template class accum { int value_; public: accum() : value_(Initial) {} void add(int n) { value_ = Func()(value_, n); } int result() { return value_; } }; typedef accum > accum_add; typedef accum, 1> accum_mult; Non type parameters Default parameters Initialise value_ with parameter Initialise value_ with parameter Addition typedef doesn’t change as default value can be used Addition typedef doesn’t change as default value can be used

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

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

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

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?

42 Someone Else’s 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

43 Someone Else’s 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

44 Everyone Else’s 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?

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

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

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_

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_

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

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

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 can’t do anything here so leave it undefined We can’t 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

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

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?

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(); } };

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_; } };

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.

57 Compile time dimensional analysis

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

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

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

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

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

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)

64 Well some of you asked…

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

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

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

68 Use cases? Static loop unrolling

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?

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); } We’d like to accumulate odd and even numbers separately. We’d like to accumulate odd and even numbers separately. Both accumulate function calls will use the same static data

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

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?

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

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

75 Use Cases? Static loop unrolling Type conditional compilation Calculation of bus widths/ranges Other things I haven’t thought of

76 Questions? Comments?


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

Similar presentations


Ads by Google