Reputation: 2223
I have a very simple test case, with one Geometry class containing a very large std::vector. I am comparing the speed of copy/move constructors:
class Geometry
{
public:
Geometry(size_t size) : m_data(size) {}
Geometry(const Geometry& other) : m_data(other.m_data)
{ std::cout << "Copy constructor" << std::endl; }
Geometry(Geometry&& other) noexcept : m_data(std::move(other.m_data))
{ std::cout << "Move constructor" << std::endl; }
private:
std::vector<double> m_data;
};
int main()
{
Geometry geometry(1000000000);
{
ScopedTimer scopedTimer("copy constructor");
Geometry geometry2(geometry);
}
{
ScopedTimer scopedTimer("move constructor");
Geometry geometry2(std::move(geometry));
}
}
I was expecting the copy constructor to be very slow, and the move constructor to be virtually instantaneous, as it just needs to swap the handle to the underlying vector resources. However, this is not what I am observing here (ScopedTimer is just a simple timer based on std::chrono that returns the duration between its construction and destruction). Here is the output I am getting in release configuration (a similar trend is observed in debug configuration):
Copy constructor
6832 ms copy constructor
Move constructor
2605 ms move constructor
Move constructor is about three times faster, which is better, but not what I was expecting. Why isn't it faster than that? I was expecting the move constructor to be O(1). Why does it take longer with larger vector sizes? The code does not need to allocate anything, etc. Am I missing something?
Upvotes: 2
Views: 396
Reputation: 37487
You are measuring vector destruction time. Without it move constructor takes no time even in debug mode:
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <chrono>
class ScopedTimer
{
std::string m_text;
::std::chrono::high_resolution_clock::time_point start;
public: ScopedTimer(::std::string const & text):
m_text{text}, start{::std::chrono::high_resolution_clock::now()} {}
public: void Report(void)
{
auto const end{::std::chrono::high_resolution_clock::now()};
::std::cout << m_text << " " << ::std::chrono::duration_cast<::std::chrono::milliseconds>(end - start).count() << ::std::endl;
}
};
class Geometry
{
public:
Geometry(size_t size) : m_data(size) {}
Geometry(const Geometry& other) : m_data(other.m_data)
{ std::cout << "Copy constructor" << std::endl; }
Geometry(Geometry&& other) noexcept : m_data(std::move(other.m_data))
{ std::cout << "Move constructor" << std::endl; }
private:
std::vector<double> m_data;
};
int main()
{
Geometry geometry(1000000000);
{
ScopedTimer scopedTimer("copy constructor");
{
Geometry geometry2(geometry);
scopedTimer.Report();
}
scopedTimer.Report();
}
{
ScopedTimer scopedTimer("move constructor");
{
Geometry geometry2(std::move(geometry));
scopedTimer.Report();
}
scopedTimer.Report();
}
return 0;
}
Copy constructor
copy constructor 5099
copy constructor 6526
Move constructor
move constructor 0
move constructor 1319
Upvotes: 4