[Documentation] [TitleIndex] [WordIndex

Eigen in Realtime

This page outlines rules for using Eigen inside realtime

Memory allocations

The eigen_allocations package exists to count the allocations for various operations in Eigen. It counts allocations for different types of matrices, including dynamically sized (Matrix<double, Dynamic, Dynamic>), fixed size (Matrix<double, 3, 7>), and dynamically sized limited allocation (Matrix<double, Dynamic, Dynamic, AutoAlign, 6, 15>).

The following is the output from eigen_allocations when run against Eigen 2.0.14. The letters indicate the matrix type, where F is fixed-size, D is dynamically sized, L is limited allocation, Xv is a vector of type X, T is a transform, and Q is a quaternion.

  malloc   calloc  realloc     free memalign
       -        -        -        -        -   Initial
       1        -        -        1        -   Sanity check
       -        -        -        -        -   Created fixed-size matrix
       -        -        -        -        1   Created dynamic matrix
       -        -        -        -        -   Fixed-size initialization
       -        -        -        -        -   Dynamic initialization
       -        -        -        -        -   Limited dynamic size initialization
       -        -        -        -        -   F = F
       -        -        -        -        -   D = D  (no resize)
       -        -        -        -        -   L = L  (no resize)
       -        -        -        -        -   F.setIdentity()
       -        -        -        -        -   D.setIdentity()
       -        -        -        -        -   F = F + F
       -        -        -        -        -   D = D + D
       -        -        -        -        -   F = F + F + F
       -        -        -        -        -   D = D + D + D
       -        -        -        -        -   F = F * F
       -        -        -        1        1   D = D * D  (no resize)
       -        -        -        -        -   D = (D * D).lazy()  (no resize)
       -        -        -        1        1   D *= D  (no resize)
       -        -        -        1        1   D *= D.lazy()  (no resize)
       -        -        -        -        -   L = L * L (no resize)
       -        -        -        -        -   F = F * F * F
       -        -        -        -        -   L = L * L * L
       -        -        -        -        -   Fv = F * Fv
       -        -        -        1        1   Dv = D * Dv  (no resize)
       -        -        -        -        -   F = F * F.t
       -        -        -        1        1   D = D * D.t
       -        -        -        -        -   F.computeInverse(F)
       -        -        -        6        6   D.computeInverse(D)
       -        -        -        7        7   D = D.inverse().lazy()
       -        -        -        -        -   L.computeInverse(L)
       -        -        -        -        -   LU(F)
       -        -        -        2        5   LU(D)
       -        -        -        -        -   LU(L)
       -        -        -        -        -   F.lu()
       -        -        -        -        -   L.lu()
       -        -        -        -        -   LU(L)
       -        -        -        -        -   SVD(F)
       -        -        -        3        6   SVD(D)
       -        -        -        2        5   SVD(L)
       -        -        -        -        -   SVD().compute(F)
       -        -        -        3        3   SVD().compute(D)  (pre-allocated)
       -        -        -        2        2   SVD().compute(L)
  malloc   calloc  realloc     free memalign
       -        -        -        -        -   Constructing Transforms
       -        -        -        -        -   Initializing Transforms
       -        -        -        -        -   T = T * T
       -        -        -        -        -   Initializing Quaternions
       -        -        -        -        -   Extracting quaternion from a transform
       -        -        -        -        -   Fv = T.translation() - T.translation()

These results show that operations on fixed size matrices don't allocate and that operations on dynamically sized matrices nearly always allocate. The promising result is that limited allocation matrices rarely cause memory allocations. In fact, the only operation that does allocation is the SVD operation.

As long as we know the maximum dimensions of a matrix at compile time, we can dynamically resize the matrix at runtime in a realtime-safe manner. Using limited allocation matrices allows us to write realtime code that is flexible and also safe.

NOTE: In eigen 2.0.13, the LU operation (and computeInverse) does perform allocations for limited allocation matrices.

For readability, here is a copy of the above, only including limited allocation matrices:

  malloc   calloc  realloc     free memalign
       -        -        -        -        -   Limited dynamic size initialization
       -        -        -        -        -   L = L  (no resize)
       -        -        -        -        -   L = L * L (no resize)
       -        -        -        -        -   L = L * L * L
       -        -        -        -        -   L.computeInverse(L)
       -        -        -        -        -   LU(F)
       -        -        -        -        -   LU(L)
       -        -        -        -        -   L.lu()
       -        -        -        -        -   LU(L)
       -        -        -        2        5   SVD(L)
       -        -        -        2        2   SVD().compute(L)

2024-03-23 12:18