X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FMoof%2Fcml%2Fvector%2Fvector_unroller.h;fp=src%2FMoof%2Fcml%2Fvector%2Fvector_unroller.h;h=eed6da66577b78d6eb4f004cc4ba141a2f27bae7;hp=0000000000000000000000000000000000000000;hb=c2321281bf12a7efaedde930422c7ddbc92080d4;hpb=87bc17e55b0c1dc73ecc66df856d3f08fd7a7724 diff --git a/src/Moof/cml/vector/vector_unroller.h b/src/Moof/cml/vector/vector_unroller.h new file mode 100644 index 0000000..eed6da6 --- /dev/null +++ b/src/Moof/cml/vector/vector_unroller.h @@ -0,0 +1,259 @@ +/* -*- 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