Eigen
3.3.90 (mercurial changeset c894d8d3ec3d)

Executive summary: Eigen has intelligent compiletime mechanisms to enable lazy evaluation and removing temporaries where appropriate. It will handle aliasing automatically in most cases, for example with matrix products. The automatic behavior can be overridden manually by using the MatrixBase::eval() and MatrixBase::noalias() methods.
When you write a line of code involving a complex expression such as
Eigen determines automatically, for each subexpression, whether to evaluate it into a temporary variable. Indeed, in certain cases it is better to evaluate a subexpression into a temporary variable, while in other cases it is better to avoid that.
A traditional math library without expression templates always evaluates all subexpressions into temporaries. So with this code,
a traditional library would evaluate vec2
+ vec3 into a temporary vec4
and then copy vec4
into vec1
. This is of course inefficient: the arrays are traversed twice, so there are a lot of useless load/store operations.
Expressiontemplatesbased libraries can avoid evaluating subexpressions into temporaries, which in many cases results in large speed improvements. This is called lazy evaluation as an expression is getting evaluated as late as possible. In Eigen all expressions are lazyevaluated. More precisely, an expression starts to be evaluated once it is assigned to a matrix. Until then nothing happens beyond constructing the abstract expression tree. In contrast to most other expressiontemplatesbased libraries, however, Eigen might choose to evaluate some subexpressions into temporaries. There are two reasons for that: first, pure lazy evaluation is not always a good choice for performance; second, pure lazy evaluation can be very dangerous, for example with matrix products: doing mat = mat*mat
gives a wrong result if the matrix product is directly evaluated within the destination matrix, because of the way matrix product works.
For these reasons, Eigen has intelligent compiletime mechanisms to determine automatically which subexpression should be evaluated into a temporary variable.
So in the basic example,
Eigen chooses not to introduce any temporary. Thus the arrays are traversed only once, producing optimized code. If you really want to force immediate evaluation, use eval():
Here is now a more involved example:
Here again Eigen won't introduce any temporary, thus producing a single fused evaluation loop, which is clearly the correct choice.
The default evaluation strategy is to fuse the operations in a single loop, and Eigen will choose it except in a few circumstances.
The first circumstance in which Eigen chooses to evaluate a subexpression is when it sees an assignment a = b;
and the expression b
has the evaluatebeforeassigning flag. The most important example of such an expression is the matrix product expression. For example, when you do
Eigen will evaluate mat * mat
into a temporary matrix, and then copies it into the original mat
. This guarantees a correct result as we saw above that lazy evaluation gives wrong results with matrix products. It also doesn't cost much, as the cost of the matrix product itself is much higher. Note that this temporary is introduced at evaluation time only, that is, within operator= in this example. The expression mat * mat
still return a abstract product type.
What if you know that the result does no alias the operand of the product and want to force lazy evaluation? Then use .noalias() instead. Here is an example:
Here, since we know that mat2 is not the same matrix as mat1, we know that lazy evaluation is not dangerous, so we may force lazy evaluation. Concretely, the effect of noalias() here is to bypass the evaluatebeforeassigning flag.
The second circumstance in which Eigen chooses to evaluate a subexpression, is when it sees a nested expression such as a + b
where b
is already an expression having the evaluatebeforenesting flag. Again, the most important example of such an expression is the matrix product expression. For example, when you do
the products mat2 * mat3
and mat4 * mat5
gets evaluated separately into temporary matrices before being summed up in mat1
. Indeed, to be efficient matrix products need to be evaluated within a destination matrix at hand, and not as simple "dot products". For small matrices, however, you might want to enforce a "dotproduct" based lazy evaluation with lazyProduct(). Again, it is important to understand that those temporaries are created at evaluation time only, that is in operator =. See TopicPitfalls_auto_keyword for common pitfalls regarding this remark.
The third circumstance in which Eigen chooses to evaluate a subexpression, is when its cost model shows that the total cost of an operation is reduced if a subexpression gets evaluated into a temporary. Indeed, in certain cases, an intermediate result is sufficiently costly to compute and is reused sufficiently many times, that is worth "caching". Here is an example:
Here, provided the matrices have at least 2 rows and 2 columns, each coefficient of the expression mat3 + mat4
is going to be used several times in the matrix product. Instead of computing the sum every time, it is much better to compute it once and store it in a temporary variable. Eigen understands this and evaluates mat3 + mat4
into a temporary variable before evaluating the product.