Difference between revisions of "Pit Falls"
Line 16: | Line 16: | ||
We try to always document this, but do tell us if we forgot an occurence. | We try to always document this, but do tell us if we forgot an occurence. | ||
− | == Ternary operator | + | == Expression templates == |
+ | |||
+ | === Ternary operator === | ||
In short: avoid the use of the ternary operator (COND ? THEN : ELSE) with Eigen's expressions for the THEN and ELSE statements. To see why, let's consider the following example: | In short: avoid the use of the ternary operator (COND ? THEN : ELSE) with Eigen's expressions for the THEN and ELSE statements. To see why, let's consider the following example: | ||
Line 29: | Line 31: | ||
In this very particular case, a workaround would be to call A.reverse().eval() for the THEN statement, but the safest and fastest is really to avoid this ternary operator with Eigen's expressions and use a if/else construct. | In this very particular case, a workaround would be to call A.reverse().eval() for the THEN statement, but the safest and fastest is really to avoid this ternary operator with Eigen's expressions and use a if/else construct. | ||
− | == | + | === C++11 & auto === |
+ | |||
+ | In short: do not use the auto keywords with Eigen's expression, unless you are 100% sure about what you are doing. In particular, do not use the auto keywords as a replacement for a Matrix<> type. Here is an example: | ||
+ | <source lang="cpp"> | ||
+ | MatrixXd A, B; | ||
+ | auto C = A*B; | ||
+ | for(...) { ... w = C * v; ...} | ||
+ | </source> | ||
+ | In this example, the type of C is not a MatrixXd but an abstract expression representing a matrix product and storing references to A and B. Therefore, the product of A*B will be carried out multiple times, once per iteration of the for loop. Moreover, if the coefficients of A or B change during the iteration, then C will evaluate to different values. | ||
+ | |||
+ | === Passing expressions to functions === | ||
While you may be extremely careful and use care to make sure that all of your code that explicitly uses Eigen types is pass-by-reference you have to watch out for templates which define the argument types at compile time. | While you may be extremely careful and use care to make sure that all of your code that explicitly uses Eigen types is pass-by-reference you have to watch out for templates which define the argument types at compile time. |
Revision as of 16:51, 7 July 2015
Contents
Alignment Issues (runtime assertion)
Eigen does explicit vectorization, and while that is appreciated by many users, that also leads to some issues in special situations where data alignment is compromised. Indeed, C++98 doesn't have quite good enough support for explicit data alignment (that's coming in C++1x). In that case your program hits an assertion failure (that is, a "controlled crash") with a message that tells you to consult this page:
http://eigen.tuxfamily.org/dox/TopicUnalignedArrayAssert.html
Have a look at it and see for yourself if that's something that you can cope with. It contains detailed information about how to deal with each known cause for that issue.
Avoiding Alignment Assertions
Now what if you don't care about vectorization and so don't want to be annoyed with these alignment issues? Then you can easily get rid of them.
Header Issues (failure to compile)
With all libraries, one must check the documentation for which header to include. The same is true with Eigen, but worse: with Eigen, a method in a class may require an additional #include over what the class itself requires! For example, if you want to use the cross() method on a vector (it computes a cross-product) then you need to
#include<Eigen/Geometry>
We try to always document this, but do tell us if we forgot an occurence.
Expression templates
Ternary operator
In short: avoid the use of the ternary operator (COND ? THEN : ELSE) with Eigen's expressions for the THEN and ELSE statements. To see why, let's consider the following example:
Vector3f A; A << 1, 2, 3; Vector3f B = ((1 < 0) ? (A.reverse()) : A);
This example will return B = 3, 2, 1. Do you see why? The reason is that in c++ the type of the ELSE statement is inferred from the type of the THEN expression such that both match. Since THEN is a Reverse<Vector3f>, the ELSE statement A is converted to a Reverse<Vector3f>, and the compiler thus generates: Vector3f B = ((1 < 0) ? (A.reverse()) : Reverse<Vector3f>(A)); </source> In this very particular case, a workaround would be to call A.reverse().eval() for the THEN statement, but the safest and fastest is really to avoid this ternary operator with Eigen's expressions and use a if/else construct.
C++11 & auto
In short: do not use the auto keywords with Eigen's expression, unless you are 100% sure about what you are doing. In particular, do not use the auto keywords as a replacement for a Matrix<> type. Here is an example:
MatrixXd A, B; auto C = A*B; for(...) { ... w = C * v; ...}
In this example, the type of C is not a MatrixXd but an abstract expression representing a matrix product and storing references to A and B. Therefore, the product of A*B will be carried out multiple times, once per iteration of the for loop. Moreover, if the coefficients of A or B change during the iteration, then C will evaluate to different values.
Passing expressions to functions
While you may be extremely careful and use care to make sure that all of your code that explicitly uses Eigen types is pass-by-reference you have to watch out for templates which define the argument types at compile time.
If a template has a function that takes arguments pass-by-value, and the relevant template parameter ends up being an Eigen type, then you will of course have the same alignment problems that you would in an explicitly defined function passing Eigen types by reference.
boost::bind
Using Eigen types with other third party libraries or even the STL can present the same problem. boost::bind for example uses pass-by-value to store arguments in the returned functor. This will of course be a problem.
There are at least two ways around this:
If the value you are passing is guaranteed to be around for the life of the functor, you can use boost::ref() to wrap the value as you pass it to boost::bind. Generally this is not a solution for values on the stack as if the functor ever gets passed to a lower or independant scope, the object may be gone by the time it's attempted to be used.
The other option is to make your functions take a reference counted pointer like boost::shared_ptr as the argument. This avoids needing to worry about managing the lifetime of the object being passed.