adding the move constructor example for map insert
This commit is contained in:
committed by
JamesFlare1212
parent
cf9b51d64a
commit
af2c2bddd2
73
lectures/optimization/map_move/README.md
Normal file
73
lectures/optimization/map_move/README.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Using Move Constructors to Improve Performance in std::map::insert
|
||||||
|
|
||||||
|
When working with std::map, understanding how move semantics improve performance can be crucial for optimizing memory management and overall efficiency. The move constructor is an essential tool in this process, especially in the context of inserting elements into a std::map.
|
||||||
|
|
||||||
|
## Basic Overview of std::map Insertions
|
||||||
|
|
||||||
|
A std::map is a sorted associative container that stores key-value pairs. The keys in a map are unique, and they are automatically sorted according to a comparison function. When you insert elements into a map using insert or emplace, the container must allocate memory and potentially rearrange internal data structures to maintain the map's properties.
|
||||||
|
|
||||||
|
The insert operation typically involves:
|
||||||
|
|
||||||
|
- Creating a new key-value pair.
|
||||||
|
- Inserting it into the internal tree structure (typically a Red-Black tree).
|
||||||
|
- If an object is inserted into a map, it may need to be copied or moved, depending on the availability of move semantics and the state of the object being inserted.
|
||||||
|
|
||||||
|
## The Role of Move Constructors
|
||||||
|
|
||||||
|
- Move constructors provide a mechanism to transfer ownership of resources (like dynamically allocated memory) from one object to another, without the overhead of copying the data. In contrast, the copy constructor performs a deep copy, which involves duplicating all resources of the original object. For large objects, this can be slow and memory-intensive.
|
||||||
|
|
||||||
|
- A move constructor transfers the ownership of resources without allocating new memory, which is a much faster process compared to copying.
|
||||||
|
|
||||||
|
## How Move Constructors Improve std::map::insert Performance
|
||||||
|
|
||||||
|
When you insert an element as a a temporary object into a std::map using insert, the insert function will call the copy constructor; but if a move constructor exists, it will call the move constructor instead of the copy constructor. Since in the move constructor, a shallow copy occurs, it is faster than the copy constructor, which normally does a deep copy.
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
Compile and run these 2 programs to see the performance difference.
|
||||||
|
|
||||||
|
[map_insert_copy.cpp](map_insert_copy.cpp) [map_insert_move.cpp](map_insert_move.cpp)
|
||||||
|
|
||||||
|
Both programs just create a map and insert 10 elements (where the value is a large class object), the only difference between these two programs, is that the second program defines the move constructor where the first program does not define the move constructor. As can be seen from the following results, having the move constructor will improve the performance of the program.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$g++ -o map_insert_copy map_insert_copy.cpp
|
||||||
|
$g++ -o map_insert_move map_insert_move.cpp
|
||||||
|
|
||||||
|
$time ./map_insert_copy
|
||||||
|
|
||||||
|
real 0m0.507s
|
||||||
|
user 0m0.247s
|
||||||
|
sys 0m0.259s
|
||||||
|
$
|
||||||
|
|
||||||
|
$time ./map_insert_copy
|
||||||
|
|
||||||
|
real 0m0.505s
|
||||||
|
user 0m0.208s
|
||||||
|
sys 0m0.297s
|
||||||
|
|
||||||
|
$time ./map_insert_move
|
||||||
|
|
||||||
|
real 0m0.290s
|
||||||
|
user 0m0.178s
|
||||||
|
sys 0m0.112s
|
||||||
|
|
||||||
|
$time ./map_insert_move
|
||||||
|
|
||||||
|
real 0m0.277s
|
||||||
|
user 0m0.176s
|
||||||
|
sys 0m0.101s
|
||||||
|
|
||||||
|
$time ./map_insert_copy
|
||||||
|
|
||||||
|
real 0m0.509s
|
||||||
|
user 0m0.248s
|
||||||
|
sys 0m0.261s
|
||||||
|
|
||||||
|
$time ./map_insert_move
|
||||||
|
|
||||||
|
real 0m0.282s
|
||||||
|
user 0m0.182s
|
||||||
|
sys 0m0.100s
|
||||||
|
```
|
||||||
42
lectures/optimization/map_move/map_copy.cpp
Normal file
42
lectures/optimization/map_move/map_copy.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <chrono>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class LargeClass {
|
||||||
|
public:
|
||||||
|
std::vector<int> data;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
LargeClass(int size, const std::string& n) : data(size), name(n) {
|
||||||
|
// Simulate work in the constructor
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
data[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a copy constructor for demonstration purposes
|
||||||
|
LargeClass(const LargeClass& other) : data(other.data), name(other.name) {
|
||||||
|
std::cout << "Copy constructor called\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// For demonstration, printing the object contents
|
||||||
|
void print() const {
|
||||||
|
std::cout << name << ": " << data[0] << "..." << data[data.size() - 1] << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const int num_elements = 10000000;
|
||||||
|
const int map_size = 10;
|
||||||
|
|
||||||
|
std::map<int, LargeClass> map_insert;
|
||||||
|
|
||||||
|
for (int i = 0; i < map_size; ++i) {
|
||||||
|
map_insert.insert({i, LargeClass(num_elements, "Insert_" + std::to_string(i))});
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
41
lectures/optimization/map_move/map_insert_copy.cpp
Normal file
41
lectures/optimization/map_move/map_insert_copy.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class LargeClass {
|
||||||
|
public:
|
||||||
|
std::vector<int> data;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
LargeClass(int size, const std::string& n) : data(size), name(n) {
|
||||||
|
// simulate work in the constructor
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
data[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// define a copy constructor for demonstration purposes
|
||||||
|
LargeClass(const LargeClass& other) : data(other.data), name(other.name) {
|
||||||
|
// std::cout << "Copy constructor called\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// for demonstration, printing the object contents
|
||||||
|
void print() const {
|
||||||
|
std::cout << name << ": " << data[0] << "..." << data[data.size() - 1] << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const int num_elements = 10000000;
|
||||||
|
const int map_size = 10;
|
||||||
|
|
||||||
|
std::map<int, LargeClass> map_insert;
|
||||||
|
|
||||||
|
for (int i = 0; i < map_size; ++i) {
|
||||||
|
map_insert.insert({i, LargeClass(num_elements, "Insert_" + std::to_string(i))});
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
46
lectures/optimization/map_move/map_insert_move.cpp
Normal file
46
lectures/optimization/map_move/map_insert_move.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class LargeClass {
|
||||||
|
public:
|
||||||
|
std::vector<int> data;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
LargeClass(int size, const std::string& n) : data(size), name(n) {
|
||||||
|
// simulate work in the constructor
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
data[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// define a copy constructor for demonstration purposes
|
||||||
|
LargeClass(const LargeClass& other) : data(other.data), name(other.name) {
|
||||||
|
// std::cout << "Copy constructor called\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// define a move constructor
|
||||||
|
LargeClass(LargeClass&& other) noexcept : data(std::move(other.data)), name(std::move(other.name)) {
|
||||||
|
// std::cout << "Move constructor called\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// for demonstration, printing the object contents
|
||||||
|
void print() const {
|
||||||
|
std::cout << name << ": " << data[0] << "..." << data[data.size() - 1] << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const int num_elements = 10000000;
|
||||||
|
const int map_size = 10;
|
||||||
|
|
||||||
|
std::map<int, LargeClass> map_insert;
|
||||||
|
|
||||||
|
for (int i = 0; i < map_size; ++i) {
|
||||||
|
map_insert.insert({i, LargeClass(num_elements, "Insert_" + std::to_string(i))});
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
47
lectures/optimization/map_move/map_move.cpp
Normal file
47
lectures/optimization/map_move/map_move.cpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <chrono>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class LargeClass {
|
||||||
|
public:
|
||||||
|
std::vector<int> data;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
LargeClass(int size, const std::string& n) : data(size), name(n) {
|
||||||
|
// Simulate work in the constructor
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
data[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a copy constructor for demonstration purposes
|
||||||
|
LargeClass(const LargeClass& other) : data(other.data), name(other.name) {
|
||||||
|
std::cout << "Copy constructor called\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a move constructor
|
||||||
|
LargeClass(LargeClass&& other) noexcept : data(std::move(other.data)), name(std::move(other.name)) {
|
||||||
|
std::cout << "Move constructor called\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// For demonstration, printing the object contents
|
||||||
|
void print() const {
|
||||||
|
std::cout << name << ": " << data[0] << "..." << data[data.size() - 1] << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const int num_elements = 10000000;
|
||||||
|
const int map_size = 10;
|
||||||
|
|
||||||
|
std::map<int, LargeClass> map_insert;
|
||||||
|
|
||||||
|
for (int i = 0; i < map_size; ++i) {
|
||||||
|
map_insert.insert({i, LargeClass(num_elements, "Insert_" + std::to_string(i))});
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user