Could an optimizing compiler remove all runtime costs from std::unique_ptr?

时间秒杀一切 提交于 2019-12-23 09:27:42

问题


Reading about std::unique_ptr at http://en.cppreference.com/w/cpp/memory/unique_ptr, my naive impression is that a smart enough compiler could replace correct uses of unique_ptr with bare pointers and just put in a delete when the unique_ptrs get destroyed. Is this actually the case? If so, do any of the mainstream optimizing compilers actually do this? If not, would it be possible to write something with some/all of unique_ptrs compile-time safety benefits that could be optimized to have no runtime cost (in space or time)?

Note to those (properly) worried about premature optimization: The answer here won't stop me from using std::unique_ptr, I'm just curious if it's a really awesome tool or just an awesome one.

EDIT 2013/07/21 20:07 EST:

OK, so I tested with the following program (please let me know if there's something wrong with this):

#include <climits>
#include <chrono>
#include <memory>
#include <iostream>

static const size_t iterations = 100;

int main (int argc, char ** argv) {
    std::chrono::steady_clock::rep smart[iterations];
    std::chrono::steady_clock::rep dumb[iterations];
    volatile int contents;
    for (size_t i = 0; i < iterations; i++) {
        auto start = std::chrono::steady_clock::now();
        {
            std::unique_ptr<int> smart_ptr(new int(5));
            for (unsigned int j = 0; j < UINT_MAX; j++)
                contents = *smart_ptr;
        }
        auto middle = std::chrono::steady_clock::now();
        {
            int *dumb_ptr = new int(10);
            try {
                for (unsigned int j = 0; j < UINT_MAX; j++)
                    contents = *dumb_ptr;
                delete dumb_ptr;
            } catch (...) {
                delete dumb_ptr;
                throw;
            }
        }
        auto end = std::chrono::steady_clock::now();
        smart[i] = (middle - start).count();
        dumb[i] = (end - middle).count();
    }
    std::chrono::steady_clock::rep smartAvg;
    std::chrono::steady_clock::rep dumbAvg;
    for (size_t i = 0; i < iterations; i++) {
        smartAvg += smart[i];
        dumbAvg += dumb[i];
    }
    smartAvg /= iterations;
    dumbAvg /= iterations;

    std::cerr << "Smart: " << smartAvg << " Dumb: " << dumbAvg << std::endl;
    return contents;
}

Compiling with g++ 4.7.3 using g++ --std=c++11 -O3 test.cc gave Smart: 1130859 Dumb: 1130005, which means the smart pointer is within 0.076% of the dumb pointer, which is almost surely noise.


回答1:


It would certainly be my expectation from any reasonably competent compiler, since it is just a wrapper around a simple pointer and a destructor that calls delete, so the machne code generated by the compiler for:

x *p = new X;
... do stuff with p. 
delete p; 

and

unique_ptr<X> p(new X);
... do stuff with p; 

will be exactly the same code.




回答2:


Strictly speaking, the answer is no.

Recall that unique_ptr is a template parametrized not only on the type of pointer but also on the type of the deleter. Its declaration is:

template <class T, class D = default_delete<T>> class unique_ptr;

In addition unique_ptr<T, D> contains not only a T* but also a D. The code below (which compiles on MSVC 2010 and GCC 4.8.1) illustrates the point:

#include <memory>

template <typename T>
struct deleter {
    char filler;
    void operator()(T* ptr) {}
};

int main() {
    static_assert(sizeof(int*) != sizeof(std::unique_ptr<int, deleter<int>>), "");
    return 0;
}

When you move a unique_ptr<T, D> the cost is not only that of copying the T* from source to target (as it would be with a raw pointer) since it must also copy/move a D.

It's true that smart implementations can detect if D is empty and has a copy/move constructor that doesn't do anything (this is the case of default_delete<T>) and, in such case, avoid the overhead of copying a D. In addition, it can save memory by not adding any extra byte for D.

unique_ptr's destructor must check whether the T* is null or not before calling the deleter. For defalt_delete<T> I believe, the optimizer might eliminate this test since it's OK to delete a null pointer.

However, there is one extra thing that std::unique_ptr<T, D>'s move constructor must do and T*'s doesn't. Since the ownership is passed from source to target, the source must be set to null. Similar arguments hold for assignments of unique_ptr.

Having said that, for the default_delete<T>, the overhead is so small that I believe will be very difficult to be detected by measurements.



来源:https://stackoverflow.com/questions/17777287/could-an-optimizing-compiler-remove-all-runtime-costs-from-stdunique-ptr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!