trainings/AdvancedCppV2/Presentation/smart_pointers_efficiency.md

6.3 KiB
Raw Permalink Blame History

Efficiency


Raw pointer

#include <memory>
#include <vector>

struct Data {
    char tab_[42];
};

int main(void) {
    constexpr unsigned size = 10u * 1000u * 1000u;
    std::vector<Data *> v;
    v.reserve(size);
    for (unsigned i = 0; i < size; ++i) {
        auto p = new Data;
        v.push_back(std::move(p));
    }
    for (auto p: v)
        delete p;
}

Unique pointer

#include <memory>
#include <vector>

struct Data {
    char tab_[42];
};

int main(void) {
    constexpr unsigned size = 10u * 1000u * 1000u;
    std::vector<std::unique_ptr<Data>> v;
    v.reserve(size);
    for (unsigned i = 0; i < size; ++i) {
        std::unique_ptr<Data> p{new Data};
        v.push_back(std::move(p));
    }
}

Shared pointer

#include <memory>
#include <vector>

struct Data {
    char tab_[42];
};

int main(void) {
    constexpr unsigned size = 10u * 1000u * 1000u;
    std::vector<std::shared_ptr<Data>> v;
    v.reserve(size);
    for (unsigned i = 0; i < size; ++i) {
        std::shared_ptr<Data> p{new Data};
        v.push_back(std::move(p));
    }
}

Shared pointer make_shared

#include <memory>
#include <vector>

struct Data {
    char tab_[42];
};

int main(void) {
    constexpr unsigned size = 10u * 1000u * 1000u;
    std::vector<std::shared_ptr<Data>> v;
    v.reserve(size);
    for (unsigned i = 0; i < size; ++i) {
        auto p = std::make_shared<Data>();
        v.push_back(std::move(p));
    }
}

Weak pointer

#include <memory>
#include <vector>

struct Data {
    char tab_[42];
};

int main(void) {
    constexpr unsigned size = 10u * 1000u * 1000u;
    std::vector<std::shared_ptr<Data>> vs;
    std::vector<std::weak_ptr<Data>> vw;
    vs.reserve(size);
    vw.reserve(size);
    for (unsigned i = 0; i < size; ++i) {
        std::shared_ptr<Data> p{new Data};
        std::weak_ptr<Data> w{p};
        vs.push_back(std::move(p));
        vw.push_back(std::move(w));
    }
}

Measurements

  • gcc-4.8.2
  • compilation with std=c++11 O3 DNDEBUG
  • measuring with:
    • time (real)
    • htop (mem)
    • valgrind (allocations count)

Results

test name time [s] allocations memory [MB]
raw pointer 0.54 10 000 001 686
unique pointer 0.56 10 000 001 686
shared pointer 1.00 20 000 001 1072
make shared 0.76 10 000 001 914
weak pointer 1.28 20 000 002 1222

Conclusions

  • RAII
    • acquire resource in constructor
    • release resource in destructor
  • Rule of 5, Rule of 0
  • Smart pointers:
    • std::unique_ptr primary choice, no overhead, can convert to std::shared_ptr
    • std::shared_ptr introduces memory and runtime overhead
    • std::weak_ptr breaking cycles, can convert to/from std::shared_ptr
  • Create smart pointers with std::make_shared() and std::make_unique()
  • Raw pointer should mean „access only” (no ownership)
  • Use reference instead of pointers if possible

Post-work

  • Transform the list from List.cpp into double-linked list. You should implement:
    • inserting Nodes at the beginning of the list
    • searching elements in reverse
    • Apply proper smart pointers for the reverse direction.
  • Implement your own unique_ptr. Requirements:
    • Templatized (should hold a pointer to a template type)
    • RAII (acquire in constructor, release in destructor)
    • Copying not allowed
    • Moving allowed
    • Member functions: operator*(), operator->(), get(), release(), reset()
  • Read one of these articles on move semantics: