1 /* -*- C++ -*- ------------------------------------------------------------
3 Copyright (c) 2007 Jesse Anders and Demian Nave http://cmldev.net/
5 The Configurable Math Library (CML) is distributed under the terms of the
6 Boost Software License, v1.0 (see cml/LICENSE for details).
8 *-----------------------------------------------------------------------*/
12 * @todo Need to implement unrolling for efficient col-major array access.
14 * @todo Does it make sense to unroll an assignment if either side of the
15 * assignment has a fixed size, or just when the target matrix is fixed
19 #ifndef matrix_unroller_h
20 #define matrix_unroller_h
22 #include <cml/et/traits.h>
23 #include <cml/et/size_checking.h>
24 #include <cml/et/scalar_ops.h>
26 #if !defined(CML_2D_UNROLLER) && !defined(CML_NO_2D_UNROLLER)
27 #error "The matrix unroller has not been defined."
30 #if defined(CML_2D_UNROLLER) && !defined(CML_MATRIX_UNROLL_LIMIT)
31 #error "CML_MATRIX_UNROLL_LIMIT is undefined."
38 /** Unroll a binary assignment operator on a fixed-size matrix.
40 * This uses a forward iteration to make better use of the cache.
43 * @sa cml::et::OpAssign
45 * @bug Need to verify that OpT is actually an assignment operator.
46 * @bug The 2D unroller needs to be specified for efficient col-major
49 template<class OpT
, typename E
, class AT
, typename BO
, typename L
, class SrcT
>
50 class MatrixAssignmentUnroller
54 /* The matrix type being assigned to: */
55 typedef cml::matrix
<E
,AT
,BO
,L
> matrix_type
;
57 /* Record traits for the arguments: */
58 typedef ExprTraits
<matrix_type
> dest_traits
;
59 typedef ExprTraits
<SrcT
> src_traits
;
61 #if defined(CML_2D_UNROLLER)
63 /* Forward declare: */
64 template<int R
, int C
, int LastRow
, int LastCol
, bool can_unroll
>
67 /* XXX This needs to be specified for efficient col-major access also! */
69 /** Evaluate the binary operator at element R,C. */
70 template<int R
, int C
, int LastRow
, int LastCol
>
71 struct Eval
<R
,C
,LastRow
,LastCol
,true> {
72 void operator()(matrix_type
& dest
, const SrcT
& src
) const {
74 /* Apply to current R,C: */
75 OpT().apply(dest(R
,C
), src_traits().get(src
,R
,C
));
77 /* Evaluate at R,C+1: */
78 Eval
<R
,C
+1,LastRow
,LastCol
,true>()(dest
,src
);
82 /** Evaluate the binary operator at element R,LastCol. */
83 template<int R
, int LastRow
, int LastCol
>
84 struct Eval
<R
,LastCol
,LastRow
,LastCol
,true> {
85 void operator()(matrix_type
& dest
, const SrcT
& src
) const {
87 /* Apply to R,LastCol: */
88 OpT().apply(dest(R
,LastCol
), src_traits().get(src
,R
,LastCol
));
90 /* Evaluate at R+1,0; i.e. move to next row and start the
91 * col iteration from 0:
93 Eval
<R
+1,0,LastRow
,LastCol
,true>()(dest
,src
);
97 /** Evaluate the binary operator at element LastRow,C. */
98 template<int C
, int LastRow
, int LastCol
>
99 struct Eval
<LastRow
,C
,LastRow
,LastCol
,true> {
100 void operator()(matrix_type
& dest
, const SrcT
& src
) const {
102 /* Apply to LastRow,C: */
103 OpT().apply(dest(LastRow
,C
), src_traits().get(src
,LastRow
,C
));
105 /* Evaluate at LastRow,C+1: */
106 Eval
<LastRow
,C
+1,LastRow
,LastCol
,true>()(dest
,src
);
110 /** Evaluate the binary operator at element LastRow,LastCol. */
111 template<int LastRow
, int LastCol
>
112 struct Eval
<LastRow
,LastCol
,LastRow
,LastCol
,true> {
113 void operator()(matrix_type
& dest
, const SrcT
& src
) const {
115 /* Apply to LastRow,LastCol: */
117 dest(LastRow
,LastCol
),
118 src_traits().get(src
,LastRow
,LastCol
));
123 /** Evaluate operators on large matrices using a loop. */
124 template<int R
, int C
, int LastRow
, int LastCol
>
125 struct Eval
<R
,C
,LastRow
,LastCol
,false> {
126 void operator()(matrix_type
& dest
, const SrcT
& src
) const {
127 for(size_t i
= 0; i
<= LastRow
; ++i
) {
128 for(size_t j
= 0; j
<= LastCol
; ++j
) {
129 OpT().apply(dest(i
,j
), src_traits().get(src
,i
,j
));
135 #endif // CML_2D_UNROLLER
137 #if defined(CML_NO_2D_UNROLLER)
139 /** Evaluate the binary operator using a loop. */
140 template<int R
, int C
, int LastRow
, int LastCol
> struct Eval
{
141 void operator()(matrix_type
& dest
, const SrcT
& src
) const {
142 for(size_t i
= 0; i
<= LastRow
; ++i
) {
143 for(size_t j
= 0; j
<= LastCol
; ++j
) {
144 OpT().apply(dest(i
,j
), src_traits().get(src
,i
,j
));
150 #endif // CML_NO_2D_UNROLLER
155 /** Unroll assignment for a fixed-sized matrix. */
157 cml::matrix
<E
,AT
,BO
,L
>& dest
, const SrcT
& src
, cml::fixed_size_tag
)
159 typedef cml::matrix
<E
,AT
,BO
,L
> matrix_type
;
161 LastRow
= matrix_type::array_rows
-1,
162 LastCol
= matrix_type::array_cols
-1,
163 Max
= (LastRow
+1)*(LastCol
+1)
166 #if defined(CML_2D_UNROLLER)
167 typedef typename MatrixAssignmentUnroller
<OpT
,E
,AT
,BO
,L
,SrcT
>
168 ::template Eval
<0, 0, LastRow
, LastCol
,
169 (Max
<= CML_MATRIX_UNROLL_LIMIT
)> Unroller
;
172 #if defined(CML_NO_2D_UNROLLER)
174 typedef typename MatrixAssignmentUnroller
<OpT
,E
,AT
,BO
,L
,SrcT
>
175 ::template Eval
<0, 0, LastRow
, LastCol
> Unroller
;
178 /* Use a run-time check if src is a run-time sized expression: */
179 typedef typename ExprTraits
<SrcT
>::size_tag src_size
;
180 typedef typename select_if
<
181 same_type
<src_size
,dynamic_size_tag
>::is_true
,
182 dynamic_size_tag
, fixed_size_tag
>::result size_tag
;
184 /* Check the expression size (the returned size isn't needed): */
185 CheckedSize(dest
,src
,size_tag());
186 /* Note: for two fixed-size expressions, the if-statements and
187 * comparisons should be completely eliminated as dead code. If
188 * src is a dynamic-sized expression, the check will still happen.
191 Unroller()(dest
,src
);
196 /* XXX Blah, a temp. hack to fix the auto-resizing stuff below. */
197 matrix_size
hack_actual_size(const SrcT
& /*src*/, scalar_result_tag
) {
198 return matrix_size(1,1);
201 matrix_size
hack_actual_size(const SrcT
& src
, matrix_result_tag
) {
202 typedef ExprTraits
<SrcT
> src_traits
;
203 return src_traits().size(src
);
206 matrix_size
CheckOrResize(
207 matrix_type
& dest
, const SrcT
& src
, cml::resizable_tag
)
209 #if defined(CML_AUTOMATIC_MATRIX_RESIZE_ON_ASSIGNMENT)
210 /* Get the size of src. This also causes src to check its size: */
211 matrix_size N
= hack_actual_size(
212 src
, typename
src_traits::result_tag());
214 /* Set the destination matrix's size: */
215 dest
.resize(N
.first
,N
.second
);
217 matrix_size N
= CheckedSize(dest
,src
,dynamic_size_tag());
222 matrix_size
CheckOrResize(
223 matrix_type
& dest
, const SrcT
& src
, cml::not_resizable_tag
)
225 return CheckedSize(dest
,src
,dynamic_size_tag());
227 /* XXX Blah, a temp. hack to fix the auto-resizing stuff below. */
231 /** Use a loop for dynamic-sized matrix assignment.
233 * @note The target matrix must already have the correct size.
235 * @todo This needs to be specialized for efficient row-major or col-major
238 void operator()(matrix_type
& dest
, const SrcT
& src
, cml::dynamic_size_tag
)
240 typedef ExprTraits
<SrcT
> src_traits
;
241 matrix_size N
= this->CheckOrResize(
242 dest
,src
,typename
matrix_type::resizing_tag());
243 for(size_t i
= 0; i
< N
.first
; ++i
) {
244 for(size_t j
= 0; j
< N
.second
; ++j
) {
245 OpT().apply(dest(i
,j
), src_traits().get(src
,i
,j
));
246 /* Note: we don't need get(), since dest is a matrix. */
254 /** This constructs an assignment unroller for fixed-size arrays.
256 * The operator must be an assignment op (otherwise, this doesn't make any
257 * sense). Also, automatic unrolling is only performed for fixed-size
258 * matrices; a loop is used for dynamic-sized matrices.
261 * @sa cml::et::OpAssign
263 * @bug Need to verify that OpT is actually an assignment operator.
265 template<class OpT
, class SrcT
, typename E
, class AT
, typename BO
, typename L
>
266 inline void UnrollAssignment(cml::matrix
<E
,AT
,BO
,L
>& dest
, const SrcT
& src
)
268 /* Record the destination matrix type, and the expression traits: */
269 typedef cml::matrix
<E
,AT
,BO
,L
> matrix_type
;
271 /* Record the type of the unroller: */
272 typedef detail::MatrixAssignmentUnroller
<OpT
,E
,AT
,BO
,L
,SrcT
> unroller
;
274 /* Finally, do the unroll call: */
275 unroller()(dest
, src
, typename
matrix_type::size_tag());
276 /* XXX It may make sense to unroll if either side is a fixed size. */
284 // -------------------------------------------------------------------------