/* -*- C++ -*- ------------------------------------------------------------ Copyright (c) 2007 Jesse Anders and Demian Nave http://cmldev.net/ The Configurable Math Library (CML) is distributed under the terms of the Boost Software License, v1.0 (see cml/LICENSE for details). *-----------------------------------------------------------------------*/ /** @file * @brief * * Defines vector unrollers. * * @todo Add unrolling for dynamic vectors, and for vectors longer than * CML_VECTOR_UNROLL_LIMIT. * * @todo Does it make sense to unroll an assignment if either side of the * assignment has a fixed size, or just when the target vector is fixed * size? */ #ifndef vector_unroller_h #define vector_unroller_h #include #include #include #if !defined(CML_VECTOR_UNROLL_LIMIT) #error "CML_VECTOR_UNROLL_LIMIT is undefined." #endif namespace cml { namespace et { namespace detail { /** Unroll a binary assignment operator on a fixed-size vector. * * This uses forward iteration to make efficient use of the cache. * * @sa cml::vector * @sa cml::et::OpAssign * * @bug Need to verify that OpT is actually an assignment operator. */ template class VectorAssignmentUnroller { protected: /* Forward declare: */ template struct Eval; /* The vector type being assigned to: */ typedef cml::vector vector_type; /* Record traits for the arguments: */ typedef ExprTraits dest_traits; typedef ExprTraits src_traits; /** Evaluate the binary operator for the first Len-1 elements. */ template struct Eval { void operator()(vector_type& dest, const SrcT& src) const { /* Apply to current N: */ OpT().apply(dest[N], src_traits().get(src,N)); /* Note: we don't need get(), since dest is a vector. */ /* Apply to N+1: */ Eval()(dest, src); } }; /** Evaluate the binary operator at element Last. */ template struct Eval { void operator()(vector_type& dest, const SrcT& src) const { /* Apply to last element: */ OpT().apply(dest[Last], src_traits().get(src,Last)); /* Note: we don't need get(), since dest is a vector. */ } }; /** Evaluate the binary operator using a loop. * * This is used when the vector's length is longer than * CML_VECTOR_UNROLL_LIMIT */ template struct Eval { void operator()(vector_type& dest, const SrcT& src) const { for(size_t i = 0; i <= Last; ++i) { OpT().apply(dest[i], src_traits().get(src,i)); /* Note: we don't need get(), since dest is a vector. */ } } }; public: /** Unroll assignment to a fixed-sized vector. */ void operator()(vector_type& dest, const SrcT& src, cml::fixed_size_tag) { typedef cml::vector vector_type; enum { Len = vector_type::array_size }; typedef typename VectorAssignmentUnroller::template Eval<0, Len-1, (Len <= CML_VECTOR_UNROLL_LIMIT)> Unroller; /* Note: Len is the array size, so Len-1 is the last element. */ /* Use a run-time check if src is a run-time sized expression: */ typedef typename ExprTraits::size_tag src_size; typedef typename select_if< same_type::is_true, dynamic_size_tag, fixed_size_tag>::result size_tag; /* Check the expression size (the returned size isn't needed): */ CheckedSize(dest,src,size_tag()); /* Note: for two fixed-size expressions, the if-statements and * comparisons should be completely eliminated as dead code. If src * is a dynamic-sized expression, the check will still happen. */ /* Now, call the unroller: */ Unroller()(dest,src); } private: /* XXX Blah, a temp. hack to fix the auto-resizing stuff below. */ size_t CheckOrResize( vector_type& dest, const SrcT& src, cml::resizable_tag) { #if defined(CML_AUTOMATIC_VECTOR_RESIZE_ON_ASSIGNMENT) /* Get the size of src. This also causes src to check its size: */ size_t N = std::max(dest.size(),src_traits().size(src)); /* Set the destination vector's size: */ cml::et::detail::Resize(dest,N); #else size_t N = CheckedSize(dest,src,dynamic_size_tag()); #endif return N; } size_t CheckOrResize( vector_type& dest, const SrcT& src, cml::not_resizable_tag) { return CheckedSize(dest,src,dynamic_size_tag()); } /* XXX Blah, a temp. hack to fix the auto-resizing stuff below. */ public: /** Just use a loop to assign to a runtime-sized vector. */ void operator()(vector_type& dest, const SrcT& src, cml::dynamic_size_tag) { /* Shorthand: */ typedef ExprTraits src_traits; size_t N = this->CheckOrResize( dest,src,typename vector_type::resizing_tag()); for(size_t i = 0; i < N; ++i) { OpT().apply(dest[i], src_traits().get(src,i)); /* Note: we don't need get(), since dest is a vector. */ } } }; /** Unroll a vector accumulation/reduction operator. * * This uses forward iteration to make efficient use of the cache. */ template struct VectorAccumulateUnroller { /* Forward declare: */ template struct Eval; /* Record traits for the arguments: */ typedef ExprTraits left_traits; typedef ExprTraits right_traits; /* Figure out the return type: */ typedef typename AccumT::value_type result_type; /** Evaluate for the first Len-1 elements. */ template struct Eval { result_type operator()( const LeftT& left, const RightT& right) const { /* Apply to last value: */ return AccumT().apply( OpT().apply(left[N], right_traits().get(right,N)), Eval()(left, right)); /* Note: we don't need get(), since dest is a vector. */ } }; /** Evaluate the binary operator at element Last. */ template struct Eval { result_type operator()( const LeftT& left, const RightT& right) const { return OpT().apply(left[Last],right_traits().get(right,Last)); /* Note: we don't need get(), since dest is a vector. */ } }; /** Evaluate using a loop. */ template struct Eval { result_type operator()( const LeftT& left, const RightT& right) const { result_type accum = OpT().apply(left[0],right[0]); for(size_t i = 1; i <= Last; ++i) { /* XXX This might not be optimized properly by some compilers, * but to do anything else requires changing the requirements * of a scalar operator. */ accum = AccumT().apply(accum, OpT().apply( left[i],right_traits().get(right,i))); /* Note: we don't need get(), since dest is a vector. */ } } }; }; } /** Construct an assignment unroller. * * The operator must be an assignment op, otherwise, this doesn't make any * sense. * * @bug Need to verify that OpT is actually an assignment operator. */ template inline void UnrollAssignment(cml::vector& dest, const SrcT& src) { /* Record the destination vector type, and the expression traits: */ typedef cml::vector vector_type; /* Record the type of the unroller: */ typedef detail::VectorAssignmentUnroller unroller; /* Do the unroll call: */ unroller()(dest, src, typename vector_type::size_tag()); /* XXX It may make sense to unroll if either side is a fixed size. */ } } // namespace et } // namespace cml #endif // ------------------------------------------------------------------------- // vim:ft=cpp