Re: [eigen] ThreadSafety of Eigen?

[ Thread Index | Date Index | More Archives ]

Some thoughts on the subject:

Benoit is saying that Eigen is reentrant - as it should be. That is what most people mean by thread-safe, even if it isn't thread safe in the sense that different threads can safely manipulate the same matrix.
However, this isn't entirely true.

<begin nitpicking> 
There are a couple of problematic uses of static data in Eigen.

There are numerous uses of const static global or member variables. e.g

class MatrixFunction{
  static const int Rows = Traits::RowsAtCompileTime;

These are unproblematic, since these variables are initialized at program start and never change. One only has to consider the "static initialization order fiasco" - but that is hardly a risk here - and the runtime overhead at startup, which should be minimal for these examples. There are a couple of globals in src/Core/arch/AltiVec/PacketMath.h which haven't been marked as const even though they should be, but that's an easy fix.

The finer subtleties occur in examples where static variables are declared in a function. These variables will be initialized lazily. i.e. the first time the function is called.

The use of static variables in blueNorm() in src/core/stablenorm.h is just plain wrong. The good news is that this is the only example I found of blatant non-reentrancy.

MatrixBase<Derived>::blueNorm() const
  static int nmax = -1;
  static RealScalar b1, b2, s1m, s2m, overfl, rbig, relerr;
  if(nmax <= 0)
    // calculate b1, b2, etc
    nmax = nbig;

An even subtler example would be:
QuaternionBase<Derived>::slerp(Scalar t, const QuaternionBase<OtherDerived>& other) const
  static const Scalar _one_ = Scalar(1) - NumTraits<Scalar>::epsilon();

This example is almost certainly safe, at least as long Scalar is a POD. The problem is that either the _expression_ is so simple the compiler optimizes it away - in which case there is no reason declaring it static - or it isn't. Then it might actually be inefficient, because  there is a runtime check, which involves locking(GCC and MSVC treat these cases differently), to see if the variable has been initialized or not.

The fact is that *there is no way to use lazily initialized non-POD static variables that does not include locks or some atomic API*.
For reference, see and

So, to return to blueNorm(), I see two ways of fixing this
A: Mark the function as non-reentrant. This would be Eigen's only such function then, or
B: Put b1, b2 etc in a class, a global instance of which will be initialized at startup. I think this initialization might be optimized away in programs that never call the function(that needs to be checked).

Don't be tempted to stick to lazily initialization(through double-checked locking or something equally clever), since it just isn't thread-safe.

To end this mail on a positive note: reentrancy is actually one of the reasons I decided to switch to Eigen in the first place, after having used numerous other libraries lacking in this respect.


Mail converted by MHonArc 2.6.19+