Is shrink_to_fit the proper way of reducing the capacity a `std::vector` to its size?

后端 未结 3 1797
终归单人心
终归单人心 2020-12-05 05:01

In C++11 shrink_to_fit was introduce to complement certain STL containers (e.g., std::vector, std::deque, std::string). <

3条回答
  •  悲哀的现实
    2020-12-05 05:12

    • Will the arguments hold?

    As the arguments are originally mine, don't mind if I defend them, one by one:

    1. Either shrink_to_fit does nothing (...)

      As it was mentioned, the standard says (many times, but in the case of vector it's section 23.3.7.3...) that the request is non-binding to allow an implementation latitude for optimizations. This means that the implementation can define shrink_to_fit as an no-op.

    2. (...) or it gives you cache locality issues

      In the case that shrink_to_fit is not implemented as a no-op, you have to allocate a new underlying container with capacity size(), copy (or, in the best case, move) construct all your N = size() new items from the old ones, destruct all the old ones (in the move case this should be optimized, but it's possible that this involves a loop again over the old container) and then destructing the old container per se. This is done, in libstdc++-4.9, exactly as David Rodriguez has described, by

            _Tp(__make_move_if_noexcept_iterator(__c.begin()),
                __make_move_if_noexcept_iterator(__c.end()),
                __c.get_allocator()).swap(__c);
      

      and in libc++-3.5, by a function in __alloc_traits that does approximately the same.

      Oh, and an implementation absolutely cannot rely on realloc (even if it uses malloc inside ::operator new for its memory allocations) because realloc, if it cannot shrink in-place, will either leave the memory alone (no-op case) or make a bitwise copy (and miss the opportunity for readjusting pointers, etc. that the proper C++ copying/moving constructors would give).

      Sure, one can write a shrinkable memory allocator, and use it in the constructor of its vectors.

      In the easy case where the vectors are larger than the cache lines, all that movement puts pressure on the cache.

    3. and it's O(n)

      If n = size(), I think it was established above that, at the very least, you have to do one n sized allocation, n copy or move constructions, n destructions, and one old_capacity sized deallocation.

    4. usually it's cheaper just to leave the slack in memory

      Obviously, unless you are really pressed for free memory (in which case it might be wiser to save your data to the disk and re-load it later on demand...)

    • If yes, what's the proper way of shrinking an STL container's capacity to its size (at least for std::vector).

    The proper way is still shrink_to_fit... you just have to either not rely on it or know very well your implementation!

    • And if there's a better way to shrink a container, what's the reason for the existence of shrink_to_fit after-all?

    There is no better way, but the reason for the existence of shrink_to_fit is, AFAICT, that sometimes your program might feel memory pressure and it's one way of treating it. Not a very good way, but still.

    HTH!

提交回复
热议问题