Performance of Unique Pointers
What's the performance overhead of using unique pointers compared to raw pointers in C++?
The performance overhead of using std::unique_ptr
compared to raw pointers in C++ is generally minimal, but it's worth understanding the differences.
Memory Overhead
In terms of memory usage, a std::unique_ptr
typically has the same size as a raw pointer on most implementations when using the default deleter. Let's check this:
#include <iostream>
#include <memory>
int main() {
std::cout << "Size of raw pointer: " << sizeof(int*)
<< " bytes\n";
std::cout << "Size of unique_ptr: "
<< sizeof(std::unique_ptr<int>) << " bytes\n";
}
Size of raw pointer: 8 bytes
Size of unique_ptr: 8 bytes
However, if you use a custom deleter, the size may increase:
#include <iostream>
#include <memory>
int main() {
auto customDeleter = [](int* p) { delete p; };
std::cout
<< "Size of unique_ptr with custom deleter: "
<< sizeof(
std::unique_ptr<int, decltype(customDeleter)>
) << " bytes";
}
Size of unique_ptr with custom deleter: 16 bytes
Runtime Performance
In terms of runtime performance:
- Dereferencing: Dereferencing a
std::unique_ptr
is typically as fast as dereferencing a raw pointer. Modern compilers can often optimize away any overhead. - Creation and Destruction: Creating a
std::unique_ptr
usingstd::make_unique()
is generally as fast as usingnew
. Destruction is also typically as fast as usingdelete
. - Move Operations: Moving a
std::unique_ptr
is very fast, usually just involving pointer assignment.
Here's a simple benchmark to illustrate:
#include <chrono>
#include <iostream>
#include <memory>
const int ITERATIONS = 10000000;
void benchmarkRawPointer() {
using namespace std::chrono;
auto start = high_resolution_clock::now();
for (int i = 0; i < ITERATIONS; ++i) {
int* p = new int(i);
*p += 1;
delete p;
}
auto end = high_resolution_clock::now();
duration<double> diff = end - start;
std::cout << "Raw pointer time: " << diff.count()
<< " s\n";
}
void benchmarkUniquePtr() {
using namespace std::chrono;
auto start = high_resolution_clock::now();
for (int i = 0; i < ITERATIONS; ++i) {
auto p = std::make_unique<int>(i);
*p += 1;
}
auto end = high_resolution_clock::now();
duration<double> diff = end - start;
std::cout << "Unique pointer time: " << diff.count()
<< " s\n";
}
int main() {
benchmarkRawPointer();
benchmarkUniquePtr();
}
Raw pointer time: 1.94922 s
Unique pointer time: 2.23576 s
The results will vary depending on your system, but they're often very close.
In practice, the small performance overhead of std::unique_ptr
is usually outweighed by the benefits of automatic resource management and exception safety. std::unique_ptr
helps prevent resource leaks and makes your code safer and easier to reason about, which can lead to better overall performance in complex systems.
Remember, premature optimization is the root of all evil. Always measure before optimizing, and consider the trade-offs between slight performance gains and code safety/maintainability.
Memory Ownership and Smart Pointers
Learn how to manage dynamic memory using unique pointers and the concept of memory ownership