vector_unroller.h

Go to the documentation of this file.
00001 /* -*- C++ -*- ------------------------------------------------------------
00002  
00003 Copyright (c) 2007 Jesse Anders and Demian Nave http://cmldev.net/
00004 
00005 The Configurable Math Library (CML) is distributed under the terms of the
00006 Boost Software License, v1.0 (see cml/LICENSE for details).
00007 
00008  *-----------------------------------------------------------------------*/
00022 #ifndef vector_unroller_h
00023 #define vector_unroller_h
00024 
00025 #include <cml/et/traits.h>
00026 #include <cml/et/size_checking.h>
00027 #include <cml/et/scalar_ops.h>
00028 
00029 #if !defined(CML_VECTOR_UNROLL_LIMIT)
00030 #error "CML_VECTOR_UNROLL_LIMIT is undefined."
00031 #endif
00032 
00033 namespace cml {
00034 namespace et {
00035 namespace detail {
00036 
00046 template<class OpT, typename E, class AT, class SrcT>
00047 class VectorAssignmentUnroller
00048 {
00049   protected:
00050 
00051     /* Forward declare: */
00052     template<int N, int Last, bool can_unroll> struct Eval;
00053 
00054     /* The vector type being assigned to: */
00055     typedef cml::vector<E,AT> vector_type;
00056 
00057     /* Record traits for the arguments: */
00058     typedef ExprTraits<vector_type> dest_traits;
00059     typedef ExprTraits<SrcT> src_traits;
00060 
00062     template<int N, int Last> struct Eval<N,Last,true> {
00063         void operator()(vector_type& dest, const SrcT& src) const {
00064 
00065             /* Apply to current N: */
00066             OpT().apply(dest[N], src_traits().get(src,N));
00067             /* Note: we don't need get(), since dest is a vector. */
00068 
00069             /* Apply to N+1: */
00070             Eval<N+1,Last,true>()(dest, src);
00071         }
00072     };
00073 
00075     template<int Last> struct Eval<Last,Last,true> {
00076         void operator()(vector_type& dest, const SrcT& src) const {
00077 
00078             /* Apply to last element: */
00079             OpT().apply(dest[Last], src_traits().get(src,Last));
00080             /* Note: we don't need get(), since dest is a vector. */
00081         }
00082     };
00083 
00084 
00090     template<int N, int Last> struct Eval<N,Last,false> {
00091         void operator()(vector_type& dest, const SrcT& src) const {
00092             for(size_t i = 0; i <= Last; ++i) {
00093                 OpT().apply(dest[i], src_traits().get(src,i));
00094                 /* Note: we don't need get(), since dest is a vector. */
00095             }
00096         }
00097     };
00098 
00099 
00100   public:
00101 
00103     void operator()(vector_type& dest, const SrcT& src, cml::fixed_size_tag)
00104     {
00105         typedef cml::vector<E,AT> vector_type;
00106         enum { Len = vector_type::array_size };
00107         typedef typename VectorAssignmentUnroller<OpT,E,AT,SrcT>::template
00108             Eval<0, Len-1, (Len <= CML_VECTOR_UNROLL_LIMIT)> Unroller;
00109         /* Note: Len is the array size, so Len-1 is the last element. */
00110 
00111         /* Use a run-time check if src is a run-time sized expression: */
00112         typedef typename ExprTraits<SrcT>::size_tag src_size;
00113         typedef typename select_if<
00114             same_type<src_size,dynamic_size_tag>::is_true,
00115             dynamic_size_tag, fixed_size_tag>::result size_tag;
00116 
00117         /* Check the expression size (the returned size isn't needed): */
00118         CheckedSize(dest,src,size_tag());
00119         /* Note: for two fixed-size expressions, the if-statements and
00120          * comparisons should be completely eliminated as dead code.  If src
00121          * is a dynamic-sized expression, the check will still happen.
00122          */
00123 
00124         /* Now, call the unroller: */
00125         Unroller()(dest,src);
00126     }
00127 
00128 
00129   private:
00130     /* XXX Blah, a temp. hack to fix the auto-resizing stuff below. */
00131     size_t CheckOrResize(
00132             vector_type& dest, const SrcT& src, cml::resizable_tag)
00133     {
00134 #if defined(CML_AUTOMATIC_VECTOR_RESIZE_ON_ASSIGNMENT)
00135         /* Get the size of src.  This also causes src to check its size: */
00136         size_t N = std::max(dest.size(),src_traits().size(src));
00137 
00138         /* Set the destination vector's size: */
00139         cml::et::detail::Resize(dest,N);
00140 #else
00141         size_t N = CheckedSize(dest,src,dynamic_size_tag());
00142 #endif
00143 
00144         return N;
00145     }
00146 
00147     size_t CheckOrResize(
00148             vector_type& dest, const SrcT& src, cml::not_resizable_tag)
00149     {
00150         return CheckedSize(dest,src,dynamic_size_tag());
00151     }
00152     /* XXX Blah, a temp. hack to fix the auto-resizing stuff below. */
00153   public:
00154     
00155 
00157     void operator()(vector_type& dest, const SrcT& src, cml::dynamic_size_tag)
00158     {
00159         /* Shorthand: */
00160         typedef ExprTraits<SrcT> src_traits;
00161         size_t N = this->CheckOrResize(
00162                 dest,src,typename vector_type::resizing_tag());
00163         for(size_t i = 0; i < N; ++i) {
00164             OpT().apply(dest[i], src_traits().get(src,i));
00165             /* Note: we don't need get(), since dest is a vector. */
00166         }
00167     }
00168 
00169 };
00170 
00175 template<class AccumT, class OpT, class LeftT, class RightT>
00176 struct VectorAccumulateUnroller
00177 {
00178     /* Forward declare: */
00179     template<int N, int Last, bool can_unroll> struct Eval;
00180 
00181     /* Record traits for the arguments: */
00182     typedef ExprTraits<LeftT> left_traits;
00183     typedef ExprTraits<RightT> right_traits;
00184 
00185     /* Figure out the return type: */
00186     typedef typename AccumT::value_type result_type; 
00187 
00189     template<int N, int Last> struct Eval<N,Last,true> {
00190         result_type operator()(
00191                 const LeftT& left, const RightT& right) const
00192         {
00193             /* Apply to last value: */
00194             return AccumT().apply(
00195                     OpT().apply(left[N], right_traits().get(right,N)),
00196                     Eval<N+1,Last,true>()(left, right));
00197             /* Note: we don't need get(), since dest is a vector. */
00198         }
00199     };
00200 
00202     template<int Last> struct Eval<Last,Last,true> {
00203         result_type operator()(
00204                 const LeftT& left, const RightT& right) const
00205         {
00206             return OpT().apply(left[Last],right_traits().get(right,Last));
00207             /* Note: we don't need get(), since dest is a vector. */
00208         }
00209     };
00210 
00212     template<int N, int Last> struct Eval<N,Last,false> {
00213         result_type operator()(
00214                 const LeftT& left, const RightT& right) const
00215         {
00216             result_type accum = OpT().apply(left[0],right[0]);
00217             for(size_t i = 1; i <= Last; ++i) {
00218                 /* XXX This might not be optimized properly by some compilers,
00219                  * but to do anything else requires changing the requirements
00220                  * of a scalar operator.
00221                  */
00222                 accum = AccumT().apply(accum, OpT().apply(
00223                             left[i],right_traits().get(right,i)));
00224                 /* Note: we don't need get(), since dest is a vector. */
00225             }
00226         }
00227     };
00228 };
00229 
00230 }
00231 
00239 template<class OpT, class SrcT, typename E, class AT> inline
00240 void UnrollAssignment(cml::vector<E,AT>& dest, const SrcT& src)
00241 {
00242     /* Record the destination vector type, and the expression traits: */
00243     typedef cml::vector<E,AT> vector_type;
00244 
00245     /* Record the type of the unroller: */
00246     typedef detail::VectorAssignmentUnroller<OpT,E,AT,SrcT> unroller;
00247 
00248     /* Do the unroll call: */
00249     unroller()(dest, src, typename vector_type::size_tag());
00250     /* XXX It may make sense to unroll if either side is a fixed size. */
00251 }
00252 
00253 } // namespace et
00254 } // namespace cml
00255 
00256 #endif
00257 
00258 // -------------------------------------------------------------------------
00259 // vim:ft=cpp

Generated on Sat Jul 18 19:35:36 2009 for CML 1.0 by  doxygen 1.5.9