/* -*- 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(const SrcT& /*src*/, scalar_result_tag) { return matrix_size(1,1); } matrix_size hack_actual_size(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( 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()); } /* XXX Blah, a temp. hack to fix the auto-resizing stuff below. */ 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