Memory Ownership and Smart Pointers

Performance of Unique Pointers

What's the performance overhead of using unique pointers compared to raw pointers in C++?

Abstract art representing computer programming

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:

  1. Dereferencing: Dereferencing a std::unique_ptr is typically as fast as dereferencing a raw pointer. Modern compilers can often optimize away any overhead.
  2. Creation and Destruction: Creating a std::unique_ptr using std::make_unique() is generally as fast as using new. Destruction is also typically as fast as using delete.
  3. 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.

Answers to questions are automatically generated and may not have been reviewed.

3D art showing a progammer setting up a development environment
Part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 57 Lessons
  • Over 200 Quiz Questions
  • 95% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved