问题
A commenter in a recent post of mine told me I need to utilize c++11 move-semantics better to deal with a bottleneck in my code. Below is a simplified version of what needs to be fixed.
#include <iostream>
#include <Eigen/Dense>
#include <vector>
void makeCopy(std::vector<Eigen::VectorXd> &oldV){
int n = oldV.size();
std::vector<Eigen::VectorXd> mandatoryCopy;
mandatoryCopy.resize(n);
for(int i = 0; i < n; i++){
mandatoryCopy[i] = oldV[i];
}
// swap the two
oldV = mandatoryCopy;
}
int main(int argc, char **argv)
{
// starting vector
int len(1000);
Eigen::VectorXd placeHolder(50);
std::vector<Eigen::VectorXd> v(len, placeHolder);
// copy it a bunch of times
for(int iter = 0; iter < 1000; ++iter){
std::cout << "iter: " << iter << "\n";
makeCopy(v);
}
return 0;
}
Question: inside the for loop of makeCopy
, oldV[i]
is an lvalue, so how could I do something like mandatoryCopy[i]&& = oldV[i]
? This is the primary bottleneck, right? I'm thinking something like mandatoryCopy[i]&& = std::move(oldV[i])
, but this obviously won't compile.
Edit
As per @vsoftco's suggestion, I have tried
std::vector<Eigen::VectorXd> makeCopy2(std::vector<Eigen::VectorXd> oldV){
int n = oldV.size();
std::vector<Eigen::VectorXd> mandatoryCopy;
mandatoryCopy.resize(n);
for(int i = 0; i < n; i++){
mandatoryCopy[i] = oldV[i];
}
return mandatoryCopy;
}
but I find that it is slower. Both @vsoftco and @ggael have mentioned that it would be faster to return a modified copied argument, instead of copying again, and I agree, but I doubt that this is possible for my actual code. I could ask about this later, but it would be a separate question/thread.
回答1:
You are not looking at the right line. If one copy is mandatory, then you cannot get rid of it. Nonetheless, better avoid the for loop and right:
std::vector<Eigen::VectorXd> mandatoryCopy = oldV;
On the other hand, you can omit the second copy by replacing oldV=mandatoryCopy
with:
std::swap(oldV,mandatoryCopy);
that will perform a cheap pointer exchange. You get:
void makeCopy(std::vector<Eigen::VectorXd> &oldV){
std::vector<Eigen::VectorXd> V = oldV;
// do something with V
std::swap(oldV,V);
}
For the functional style, in your second example, you must directly play with the argument which is already a copy:
std::vector<Eigen::VectorXd> makeCopy2(std::vector<Eigen::VectorXd> V){
// do something with V
return V;
}
and call it with v = makeCopy2(v);
.
Do not forget to compile with -std=c++11
to get move semantic copies.
Finally, it might be better to pack your vector<VectorXd>
within a MatrixXd
. This will dramatically reduce the number of memory allocations:
void makeCopy3(MatrixXd &oldV){
int n = oldV.cols();
MatrixXd V = oldV;
for(int i = 0; i < n; i++){
V.col(i) *= 0.99;
}
oldV.swap(V); // or oldV = std::move(V); with c++11 enabled
}
来源:https://stackoverflow.com/questions/42442748/how-to-move-eigenvectorxd-s