avatarTurbo Python

Summary

The web content provides an introduction to boost::intrusive_ptr in C++, explaining its memory management benefits over std::shared_ptr due to its more efficient reference counting mechanism.

Abstract

The article "Boost Your C++ Memory Management Skills: A Gentle Introduction to boost::intrusive_ptr" discusses the advantages of using boost::intrusive_ptr for memory management in C++. Unlike std::shared_ptr, intrusive_ptr stores the reference count within the managed object itself, leading to improved memory usage and performance. The article includes an example demonstrating how to implement intrusive_ptr with a custom class, detailing the necessary friend functions for reference counting. A performance comparison between intrusive_ptr and shared_ptr shows that intrusive_ptr is faster, attributed to its cache-friendly nature of storing the reference count within the object. The article concludes that while intrusive_ptr requires manual implementation of reference counting, it is a superior choice for latency-sensitive applications.

Opinions

  • The author suggests that boost::intrusive_ptr is more memory-efficient and performs better than std::shared_ptr due to its intrusive nature of reference counting.
  • The article implies that the performance benefits of intrusive_ptr are significant, especially in scenarios where the reference count is frequently accessed, such as in latency-sensitive applications.
  • The author emphasizes the importance of proper implementation of reference counting mechanisms when using intrusive_ptr, highlighting the responsibility placed on the developer.
  • It is noted that intrusive_ptr can be a good choice for developers who prioritize performance and are willing to manage the intricacies of intrusive reference counting.
  • The article provides a fair comparison between intrusive_ptr and shared_ptr by using std::atomic for thread-safe reference counting in both cases.

Boost Your C++ Memory Management Skills: A Gentle Introduction to boost::intrusive_ptr

An intrusive_ptr is a type of smart pointer that uses reference counting to manage the lifetime of dynamically allocated objects. Unlike shared_ptr, intrusive_ptr keeps the reference count directly within the managed object, rather than using a separate allocation to store the count. This makes intrusive_ptr more efficient in terms of memory usage and performance, but requires a different approach to implementing the reference counting mechanism.

Here’s an example of how to use intrusive_ptr in C++:

#include <boost/intrusive_ptr.hpp>
#include <iostream>

class MyClass {
 public:
  MyClass() : ref_count_(0) {}
  friend void intrusive_ptr_add_ref(MyClass* p) { ++p->ref_count_; }
  friend void intrusive_ptr_release(MyClass* p) {
    if (--p->ref_count_ == 0) delete p;
  }
  void Print() { std::cout << "Hello, world!" << std::endl; }
 private:
  int ref_count_;
};
int main() {
  boost::intrusive_ptr<MyClass> ptr(new MyClass());
  ptr->Print();
  return 0;
}

In this example, we define a class MyClass that has a reference count, and two friend functions intrusive_ptr_add_ref and intrusive_ptr_release that implement the reference counting mechanism. These functions are used by the intrusive_ptr to manage the lifetime of MyClass objects.

In the main function, we create an instance of MyClass and wrap it in an intrusive_ptr. The reference count is automatically incremented when the intrusive_ptr is created, and decremented when it goes out of scope. If the reference count reaches zero, the intrusive_ptr_release function deletes the MyClass object.

We compare the performance of intrusive_ptr and shared_ptr by copying the smart pointers for one million times (which increases the reference count). For fairness, we also use std::atomic for reference count as in the std::shared_ptr to ensure thread safety.

#include <boost/intrusive_ptr.hpp>
#include <memory>
#include <chrono>
#include <atomic>
#include <iostream>

// Object for shared_ptr
struct TestObject {
  int data;

  TestObject(int d) : data(d) {}
};

// Object for intrusive_ptr 
struct TestObjectIntrusive: public TestObject {
  TestObjectIntrusive(int d): TestObject(d) {}

  friend void intrusive_ptr_add_ref(TestObjectIntrusive* p) {
    ++p->ref_cnt;
  }

  friend void intrusive_ptr_release(TestObjectIntrusive* p) {
    if (--p->ref_cnt == 0) {
      delete p;
    }
  }

  std::atomic<std::size_t> ref_cnt;
};


// Helper function to measure the time taken for a block of code
template <typename Func>
double measureTime(Func func) {
  auto start = std::chrono::steady_clock::now();
  func();
  auto end = std::chrono::steady_clock::now();
  auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
  return duration.count() / 1000000.0;
}

int main() {
  constexpr auto N = 1000000;
  // Measure time taken to shared_ptr copy ctor
  auto timeTakenSharedPtr = measureTime([]() {
    std::shared_ptr<TestObject> obj = std::make_shared<TestObject>(10);
    for (int i = 0; i < N; i++) {
      std::shared_ptr<TestObject> tmp(obj);
    }
  });

  // Measure time taken to intrusive_ptr copy ctor
  auto timeTakenIntrusivePtr = measureTime([]() {
    boost::intrusive_ptr<TestObjectIntrusive> obj(new TestObjectIntrusive(10));
    for (int i = 0; i < N; i++) {
      boost::intrusive_ptr<TestObjectIntrusive> tmp(obj);
    }
  });

  std::cout << "Time taken for shared_ptr: " << timeTakenSharedPtr << "s" << std::endl;
  std::cout << "Time taken for intrusive_ptr: " << timeTakenIntrusivePtr << "s" << std::endl;

  return 0;
}
Time taken for shared_ptr: 0.027622s
Time taken for intrusive_ptr: 0.016726s

The code is compiled with the flag -O0. The result shows that intrusive_ptr is faster than shared_ptr . This is because the reference count of intrusive_ptris inside the object, which is cache-friendly. In contrast, the memory for the reference count in shared_ptr must be dynamically allocated and thus is accessed indirectly.

In summary, intrusive_ptr is a faster alternative for shared_ptr . But it requires the developer to implement the logics of reference counting inside the custom class. For latency-sensitive applications, intrusive_ptr is a good choice.

Related reading:

  1. A Hands-On Guide to Implementing std::shared_ptr | by PigByte | Dev Genius
Cpp
Programming
Programming Languages
Coding
Learning To Code
Recommended from ReadMedium