Performance loss from extensive use of shared_ptr as if C++ being garbage-collected? -
i wondering performance issues of reference counted shared_ptr
s, , wrote dumb program computes pow(2, n)
in inefficient way millions of memory allocations , deallocations, in order test shared_ptr
s while not making compiler optimize away, full optimization.
compiled with,
g++ t.cpp -std=c++11 -ot -dv_ -pedantic -ofast -s -fwhole-program
where -dv_ means use std::vector
container.
ran as,
valgrind ./t 10000
which means compute pow(2, 10000)
shows me,
total heap usage: 45,174,051 allocs, 45,174,051 frees, 783,160,061 bytes allocated
and
time ./t 10000
outputs
real 0m1.698s user 0m1.693s sys 0m0.004s
which surprisingly fast, considering amount of heap allocation done. system laptop i5 cpu , ubuntu linux os.
the basic pattern used here defining container classes extending shared_ptr
original class.
#if defined(v_) #define c_ std::vector #elif defined(l_) #define c_ std::list #elif defined(d_) #define c_ std::deque #else #error #endif template<typename t> class container : public std::shared_ptr<c_<t>> { public: container(): std::shared_ptr<c_<t>>(new c_<t>) { } }; class string : public std::shared_ptr<std::string> { public: string(): std::shared_ptr<std::string>(new std::string) { } string(const char* s): std::shared_ptr<std::string>(new std::string(s)) { } const char* getcstring() { return get()->c_str(); } };
this way not have worry @ whether pass value or reference, const reference, rvalue reference et cetera. passing value works fine , cheap. should pointer copy , reference count update. can add few deep copy methods needed, can see deep copy happening in program. letting whole vector being value copied because of missed reference mark not happen. i'd situations deep copies of large objects necessary not common.
well, benefits obvious. in other words, either java or c# offer better productivity c++. there must performance loss. in user applications loss can literally nothing, writing games, must finish computing every frame in 30ms or so. can't write games real garbage collected languages, java example, because see game pausing unexpectedly short noticeable moment , restoring back. feels not right. reference counted (pseudo-)garbage collection, point resources claimed or released quite guaranteed.
so thinking start project use pattern base, don't have experience in using reference counted pointers big-enough projects. in experimenting phase. using shared_ptr
s in time critical programs not idea, , no 1 knows impact after project gets big enough, point impossible change deep inside , used everywhere. (i worried part). or overly undervaluing greatness of modern computing machines?
do have or bad experience dealing smart pointers? these work fine multiple threads? how can compiler optimizations on them? modern hardware deal reference counting operations well? other helpful facts reference counted pointers?
below can see whole test program, if you're interested.
#include <cstdlib> #include <cmath> #include <iostream> #include <vector> #include <list> #include <deque> #include <queue> #include <memory> #if defined(v_) #define c_ std::vector #elif defined(l_) #define c_ std::list #elif defined(d_) #define c_ std::deque #else #error #endif void check(bool isokay) { if (!isokay) { std::exit(exit_failure); } } template<typename t> class container : public std::shared_ptr<c_<t>> { public: container(): std::shared_ptr<c_<t>>(new c_<t>) { } }; class string : public std::shared_ptr<std::string> { public: string(): std::shared_ptr<std::string>(new std::string) { } string(const char* s): std::shared_ptr<std::string>(new std::string(s)) { } const char* getcstring() const { return get()->c_str(); } }; class slowinteger : public container<int> { public: slowinteger() { } slowinteger(int n) { check(0 <= n && n < 20); if (n < 10) { get()->push_back(n); } else { get()->push_back(n - 10); get()->push_back(1); } } int toint() const { if (get()->size() == 1) { return get()->front(); } else { return get()->front() + 10 * get()->back(); } } }; class veryslowinteger : public container<slowinteger> { public: veryslowinteger(string stringnumber) { (auto = stringnumber->rbegin(); != stringnumber->rend(); ++it) { get()->push_back(slowinteger(*it - '0')); } } void x2() { int m = 0; (auto = get()->begin(); != get()->end(); ++it) { int n = it->toint() * 2; if (n < 10) { *it = slowinteger(m + n); m = 0; } else { *it = slowinteger(n - 10 + m); m = 1; } } if (m == 1) { get()->push_back(slowinteger(1)); } } void x2n(int n) { check(n > 0); (int = 0; < n; ++i) { x2(); } } string tostring() { string s; (auto = get()->rbegin(); != get()->rend(); ++it) { s->push_back(it->toint() + '0'); } return s; } }; int main(int argc, char **argv) { check(argc == 2); int n = std::atoi(argv[1]); check(n > 0); veryslowinteger nslow("1"); nslow.x2n(n); string s = nslow.tostring(); std::cout << s.getcstring() << std::endl; return exit_success; }