X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fcml%2Fmatrix%2Fmatrix_unroller.h;fp=src%2Fcml%2Fmatrix%2Fmatrix_unroller.h;h=ab85945285a67a7bcc70f08956dea156e586c94d;hp=0000000000000000000000000000000000000000;hb=6b0a0d0efafe34d48ab344fca3b479553bd4e62c;hpb=85783316365181491a3e3c0c63659972477cebba diff --git a/src/cml/matrix/matrix_unroller.h b/src/cml/matrix/matrix_unroller.h new file mode 100644 index 0000000..ab85945 --- /dev/null +++ b/src/cml/matrix/matrix_unroller.h @@ -0,0 +1,293 @@ +/* -*- 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 + * + * @todo Need to implement unrolling for efficient col-major array access. + * + * @todo Does it make sense to unroll an assignment if either side of the + * assignment has a fixed size, or just when the target matrix is fixed + * size? + */ + +#ifndef matrix_unroller_h +#define matrix_unroller_h + +#include +#include +#include + +#if !defined(CML_2D_UNROLLER) && !defined(CML_NO_2D_UNROLLER) +#error "The matrix unroller has not been defined." +#endif + +#if defined(CML_2D_UNROLLER) && !defined(CML_MATRIX_UNROLL_LIMIT) +#error "CML_MATRIX_UNROLL_LIMIT is undefined." +#endif + +namespace cml { +namespace et { +namespace detail { + +/** Unroll a binary assignment operator on a fixed-size matrix. + * + * This uses a forward iteration to make better use of the cache. + * + * @sa cml::matrix + * @sa cml::et::OpAssign + * + * @bug Need to verify that OpT is actually an assignment operator. + * @bug The 2D unroller needs to be specified for efficient col-major + * access. + */ +template +class MatrixAssignmentUnroller +{ + protected: + + /* The matrix type being assigned to: */ + typedef cml::matrix matrix_type; + + /* Record traits for the arguments: */ + typedef ExprTraits dest_traits; + typedef ExprTraits src_traits; + +#if defined(CML_2D_UNROLLER) + + /* Forward declare: */ + template + struct Eval; + + /* XXX This needs to be specified for efficient col-major access also! */ + + /** Evaluate the binary operator at element R,C. */ + template + struct Eval { + void operator()(matrix_type& dest, const SrcT& src) const { + + /* Apply to current R,C: */ + OpT().apply(dest(R,C), src_traits().get(src,R,C)); + + /* Evaluate at R,C+1: */ + Eval()(dest,src); + } + }; + + /** Evaluate the binary operator at element R,LastCol. */ + template + struct Eval { + void operator()(matrix_type& dest, const SrcT& src) const { + + /* Apply to R,LastCol: */ + OpT().apply(dest(R,LastCol), src_traits().get(src,R,LastCol)); + + /* Evaluate at R+1,0; i.e. move to next row and start the + * col iteration from 0: + */ + Eval()(dest,src); + } + }; + + /** Evaluate the binary operator at element LastRow,C. */ + template + struct Eval { + void operator()(matrix_type& dest, const SrcT& src) const { + + /* Apply to LastRow,C: */ + OpT().apply(dest(LastRow,C), src_traits().get(src,LastRow,C)); + + /* Evaluate at LastRow,C+1: */ + Eval()(dest,src); + } + }; + + /** Evaluate the binary operator at element LastRow,LastCol. */ + template + struct Eval { + void operator()(matrix_type& dest, const SrcT& src) const { + + /* Apply to LastRow,LastCol: */ + OpT().apply( + dest(LastRow,LastCol), + src_traits().get(src,LastRow,LastCol)); + } + }; + + + /** Evaluate operators on large matrices using a loop. */ + template + struct Eval { + void operator()(matrix_type& dest, const SrcT& src) const { + for(size_t i = 0; i <= LastRow; ++i) { + for(size_t j = 0; j <= LastCol; ++j) { + OpT().apply(dest(i,j), src_traits().get(src,i,j)); + } + } + } + }; + +#endif // CML_2D_UNROLLER + +#if defined(CML_NO_2D_UNROLLER) + + /** Evaluate the binary operator using a loop. */ + template struct Eval { + void operator()(matrix_type& dest, const SrcT& src) const { + for(size_t i = 0; i <= LastRow; ++i) { + for(size_t j = 0; j <= LastCol; ++j) { + OpT().apply(dest(i,j), src_traits().get(src,i,j)); + } + } + } + }; + +#endif // CML_NO_2D_UNROLLER + + + public: + + /** Unroll assignment for a fixed-sized matrix. */ + void operator()( + cml::matrix& dest, const SrcT& src, cml::fixed_size_tag) + { + typedef cml::matrix matrix_type; + enum { + LastRow = matrix_type::array_rows-1, + LastCol = matrix_type::array_cols-1, + Max = (LastRow+1)*(LastCol+1) + }; + +#if defined(CML_2D_UNROLLER) + typedef typename MatrixAssignmentUnroller + ::template Eval<0, 0, LastRow, LastCol, + (Max <= CML_MATRIX_UNROLL_LIMIT)> Unroller; +#endif + +#if defined(CML_NO_2D_UNROLLER) + /* Use a loop: */ + typedef typename MatrixAssignmentUnroller + ::template Eval<0, 0, LastRow, LastCol> Unroller; +#endif + + /* 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. + */ + + Unroller()(dest,src); + } + + + private: + /* XXX Blah, a temp. hack to fix the auto-resizing stuff below. */ + matrix_size hack_actual_size( + matrix_type& dest, const SrcT& /*src*/, scalar_result_tag + ) + { + typedef ExprTraits dest_traits; + return dest_traits().size(dest); + } + + matrix_size hack_actual_size( + matrix_type& /*dest*/, const SrcT& src, matrix_result_tag + ) + { + typedef ExprTraits src_traits; + return src_traits().size(src); + } + + matrix_size CheckOrResize( + matrix_type& dest, const SrcT& src, cml::resizable_tag) + { +#if defined(CML_AUTOMATIC_MATRIX_RESIZE_ON_ASSIGNMENT) + /* Get the size of src. This also causes src to check its size: */ + matrix_size N = hack_actual_size( + dest, src, typename src_traits::result_tag()); + + /* Set the destination matrix's size: */ + dest.resize(N.first,N.second); +#else + matrix_size N = CheckedSize(dest,src,dynamic_size_tag()); +#endif + return N; + } + + matrix_size CheckOrResize( + matrix_type& dest, const SrcT& src, cml::not_resizable_tag) + { + return CheckedSize(dest,src,dynamic_size_tag()); + } + + + public: + + + /** Use a loop for dynamic-sized matrix assignment. + * + * @note The target matrix must already have the correct size. + * + * @todo This needs to be specialized for efficient row-major or col-major + * layout access. + */ + void operator()(matrix_type& dest, const SrcT& src, cml::dynamic_size_tag) + { + typedef ExprTraits src_traits; + matrix_size N = this->CheckOrResize( + dest,src,typename matrix_type::resizing_tag()); + for(size_t i = 0; i < N.first; ++i) { + for(size_t j = 0; j < N.second; ++j) { + OpT().apply(dest(i,j), src_traits().get(src,i,j)); + /* Note: we don't need get(), since dest is a matrix. */ + } + } + } +}; + +} + +/** This constructs an assignment unroller for fixed-size arrays. + * + * The operator must be an assignment op (otherwise, this doesn't make any + * sense). Also, automatic unrolling is only performed for fixed-size + * matrices; a loop is used for dynamic-sized matrices. + * + * @sa cml::matrix + * @sa cml::et::OpAssign + * + * @bug Need to verify that OpT is actually an assignment operator. + */ +template +inline void UnrollAssignment(cml::matrix& dest, const SrcT& src) +{ + /* Record the destination matrix type, and the expression traits: */ + typedef cml::matrix matrix_type; + + /* Record the type of the unroller: */ + typedef detail::MatrixAssignmentUnroller unroller; + + /* Finally, do the unroll call: */ + unroller()(dest, src, typename matrix_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