[eigen] update |
[ Thread Index | Date Index | More lists.tuxfamily.org/eigen Archives ]
Hi List, I thought that i'd focus on my "real" job but didn't manage to ;) so here's an update on what happened, and wish me to manage to focus on my job now! I've been working locally with git and git-svn for the past 2 weeks, because I had really big changes in mind. I've completely refactored eigen2, and the most obvious result is that there are now only 714 lines of code, down from 1093, and we don't even use anymore those big preprocessor macros to define operators -- so the code size drop is even bigger! Concretely, I've unified MatrixXpr and MatrixBase into a single base class template, EigenBase. This way, one can declare/define a function f taking any kind of matrix/expression/vector/whatever as follows: template<typename T, typename U> f(EigenBase<T,U>& m); while the former design forced to define f separately for expressions and for actual objects. This is responsible for the larger part of the code size drop. Of course this is a "curious" base as in the CRTP programming pattern. There are no longer Matrix/MatrixX/Vector/VectorX classes each reimplementing its own constructors etc. There is only one "Matrix" class (it's what used to be called MatrixBase), and there are typedefs emulating Matrix/MatrixX/Vector/VectorX. It is now possible to "evaluate" expressions, i.e. you do eval(some_expression) and some_expression automatically finds which Matrix type must be created. This means that all our expression types now recursively remember the number of rows/columns as a compile-time constant, with a magic value (currently -1) meaning dynamic value (unknown at compile-time). This will be very useful later when we implement heavy operations which need to create matrices -- such as LU decomposition. This makes the aliasing system very unneccessary, so I removed it. So instead of m.alias() = m * m; you know do m = eval(m * m); which feels more natural. But wait, this is not all. I have understood that it is in general very wrong to have m * m return an expression! Indeed, it actually is an optimization to immediately evaluate the matrix product. Let me explain. If you do a combined matrix product like: m1 * m2 * m3 then the second product will evaluate each entry of m1*m2 MANY TIMES -- namely as many times as there are rows in m3. Now an entry of a matrix product is expensive to compute, so having m1*m2 return a temporary matrix (instead of an expression) is an optimization because it CACHES the results of these expensive computations! Now of course there are cases where one wouldn't want the temporary to be created, like m1 = m2 * m3; but then one should consider that the cost of a matrix multiplication (n^3 muls, (n-1)^3 adds, etc...) is so high that the cost of the useless copy (n^2 movs) is not a big deal! Conclusion: we'll have operator* between matrices return a matrix, that's all. So m = m * m; will just work. And for those who really know what they're doing we'll provide the expression-returning flavor under a different name, like m = m.lazyMul(m); and actually operator* will just do: operator*(const EigenBase& a, const EigenBase& b) { return eval(a.lazyMul(b)); } Cheers, Benoit PS Bensch: I know you won't like eval() being a global function, but I've yet to find a way to make the compiler tolerate it as a member. Probably that's doable, I'm just tired.
Attachment:
signature.asc
Description: This is a digitally signed message part.
Mail converted by MHonArc 2.6.19+ | http://listengine.tuxfamily.org/ |