This forum has been archived. All content is frozen. Please use KDE Discuss instead.

SparseVector: How to avoid unneeded temporaries? ::operator=

Tags: None
(comma "," separated)
alexanderwerner
Registered Member
Posts
11
Karma
0
Hi,

using Eigen 3.2, when i do
Code: Select all
Eigen::SparseVector<double> a(1),b(1);
a.reserve(1);
b.resize(1);
b.insert(0)=1.;
a = 3 * b;

the expression is always evaluated into a temporary variable, which is required since it cannot be assumed
that a and b are not the same variable. (See SparseVector::operator= in SparseVector.h)
This is not very nice for the performance. Inspired by Eigen::NoAlias i wrote the two following files to work around
this issue:

eigen_sparsevector_plugin.hpp
Code: Select all
inline NoAliasSparseVector<SparseVector> noalias() {
   return NoAliasSparseVector<SparseVector>(*this);
}



test.cpp
Code: Select all
namespace Eigen {
   template <typename T>
   class SparseMatrixBase;

   template <typename Expression>
   class NoAliasSparseVector {
   public:
      typedef Expression type;
      NoAliasSparseVector(type & v):v_(v){};
   
      template <typename Other>
      type & operator=(SparseMatrixBase<Other> const& other);
   protected:
      type & v_;
   };
}

#define EIGEN_SPARSEVECTOR_PLUGIN "eigen_sparsevector_plugin.hpp"

#include <Eigen/Dense>
#include <Eigen/Sparse>
#include <iostream>

namespace Eigen {
        template <typename Expression>
        template <typename Other>
        typename NoAliasSparseVector<Expression>::type &
        NoAliasSparseVector<Expression>::operator=(SparseMatrixBase<Other> const& other){
                v_.resize(other.size());
                internal::sparse_vector_assign_selector<type,Other>::run(v_,other.derived());
                return v_;
        }
}

int main(){
        typedef Eigen::SparseVector<double> sv_t;
        {
                sv_t in;
                sv_t out_normal;
                sv_t out_noalias;

                out_normal.reserve(1);
                out_noalias.reserve(1);
                out_normal.resize(1);
                out_noalias.resize(1);

                in.resize(1);
                in.coeffRef(0)=1.;

                // evaluated with temporary
                out_normal = 3 * in;

                // no temporary used in next line
                out_noalias.noalias() = 3 * in;

                std::cout << out_normal << std::endl;
                std::cout << out_noalias << std::endl;
        }
return 0;
}


This seems to work, but it my real world application i use something similar to the following code:
Code: Select all
Eigen::Matrix<Eigen::AutoDiffScalar<Eigen::SparseVector<double> > >, Eigen::Dynamic,1> a(1),b(1);
a(0).derivatives().reserve(1);
b(0).derivatives().resize(1);
b(0).derivatives().insert(0)=1.;
a = 3 * b;


Is there a feasible way how to use the approach shown above could be used here. I basically want to
avoid any dynamic memory allocation in the last line. I read about the new expression evaluator, but I
am not clear if this is related.

Thanks,

Alex
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
I guess that in your real-world example the dimension of the problem is much larger (1000 or more), and that the expression is also much more involved and so temporaries will be created along the way anyway because the expression template mechanism won't apply at the coefficient level.
alexanderwerner
Registered Member
Posts
11
Karma
0
Hi,

thanks for the quick answer. The real world example is a templated algorithm which does
not create any temporaries when double is used instead of AutoDiffScalar. So i think that
is not in the way. The expressions used in the algorithm are mainly C = A * B or
C.noalias() += A*B. However oftern A and B are blocks.
The motivation is that if no temporaries for SparseVector are involved,
the algorithm will initialize the sizes of the required derivative vector on the first run.
This would the hopefully lead to all further executions being free of malloc.

Just some details of the real-world application: The size of the derivative vector is ~100
and the matrix/vector dimensions ~50. The resulting derivative vectors are quite sparse
and reveal the structure of the expressions computed. Also the amount of cache needed
when using AutoDiffScalar<VectorXd> is quite big in comparsion to what SparseVector<double>
needs. The purpose of the algorithm is to compute robot dynamics.


Thanks,

Alex


Bookmarks



Who is online

Registered users: Bing [Bot], Google [Bot], Yahoo [Bot]