Why Not Using Shallow Copy in Numerical Software
Shallow copy has the advantage over deep copy of being considerably faster. This advantage does not justify all the dangers implied.
The first risk is that many programmers are not aware of the aliasing behavior, which is that after the assignment neither of the two arguments can be modified without affecting the other. As one of the two variables can be changed in a sub-function of a sub-function of a ... it is hard to track down all possible modifications.
Moreover, the problem is even more confusing. Since shallow copy semantic is only feasible between objects of the same type, assignments between different types must copy data deeply. In generic functions aiming for maximal generality one do not want assume or require equality or distinctness of argument types so that the copy behavior is unknown.
// File: shallow_copy_problems_type.cpp #include <iostream> #include <boost/numeric/mtl/mtl.hpp> using namespace mtl; int main(int, char**) { dense2D<double> A(3, 3), B(3, 3); dense2D<float> C(3, 3); A= 4.0; B= A; // Create an alias B*= 2.0; // Changes also A C= A; // Copies the values C*= 2.0; // A is unaffected return 0; }
In the same way mathematically neutral operations like multiplications with one or additions of zero vectors silently change the program behavior by disabling shallow copies and eliminating the aliasing behavior.
A= B; // Aliasing of A and B A= 1.0 * B; // A and B are independent
Many higher level libraries like ITL assigns vectors with the copy function instead of the assignment operator in order to guarantee deep copy.
A= B; // (Potential) shallow copy copy(B, A); // Deep copy
We refrain from this approach because this syntax does not correspond to the mathematical literature and more importantly we cannot be sure that all users of a library will replace assignments by copy.
Last but not least all shallow copy implementations we have seen so far relentlessly undermined const attributes of arguments.
// File: shallow_copy_problems_const.cpp #include <iostream> #include <boost/numeric/mtl/mtl.hpp> using namespace mtl; template <typename Matrix> void f2(Matrix& C) { C= 5.0; } // Undermine const-ness of function argument template <typename Matrix> double f(const Matrix& A) { Matrix B; B= A; f2(B); return frobenius_norm(A); } int main(int, char**) { dense2D<double> A(3, 3); A= 4.0; double alpha= f(A); // A is changed now! std::cout << alpha << "\n"; return 0; }
After calling f, A is modified despite it was passed as const argument and the const-ness was not even casted away.
For all these reasons we are convinced that reliable mathematical software can only be implemented with deep copy semantics. Unnecessary copies can be avoided by using advanced techniques as expression templates and Move Semantics.
Return to Copying in MTL4 Table of Content Proceed to Addicted to peak performance
Why Not Using Shallow Copy in Numerical Software -- MTL 4 -- Peter Gottschling and Andrew Lumsdaine
-- Gen. with
rev. 7542
on Sat Aug 11 2012 by doxygen 1.7.6.1 -- © 2010 by SimuNova UG.