18

To hold arbitrarily large objects, boost::any / std::any surely needs to allocate heap space for objects. However, for small types whose size is less or equal to a pointer (int,char,bool,...), any could instead store the value in-place in the pointer slot or in some other in-place memory and not allocate heap space. But does the implementation do this?

I have a scenario where I often store small types in an any and only sometimes larger types like strings. The code is quite hot and therefore I am asking the question. If the optimization is not performed, I might be better off with an own implementation that stores small types in-place.

gexicide
  • 38,535
  • 21
  • 92
  • 152
  • 1
    "The code is quite hot"? Do you mean it's not performing well? Have you profiled / are you compiling with optimizations? I highly doubt this extra memory access would be your game changer of performance. – mascoj Jan 26 '17 at 16:22
  • [`libc++` trunk](https://github.com/llvm-mirror/libcxx/blob/master/include/any) has [small object](https://github.com/llvm-mirror/libcxx/blob/master/include/any#L132) optimization for `std::any`. I suspect it should be same for any reasonable STL implementation... – WhiZTiM Jan 26 '17 at 16:28
  • 2
    @mascoj "hot code" is a code portion that is executed significantly more often than the rest, and thus is the most prone to become a bottleneck. – Quentin Jan 26 '17 at 16:31
  • @mascoj: I am creating / disposing of these objects very often. The allocations/deallocations might slow me down. – gexicide Jan 26 '17 at 16:37

3 Answers3

17

There is no guarantee but the C++17 draft states in [any.class] that

Implementations should avoid the use of dynamically allocated memory for a small contained object. [ Example: where the object constructed is holding only an int.  — end example ] Such small-object optimization shall only be applied to types T for which is_­nothrow_­move_­constructible_­v<T> is true.

Unfortunately it does not give a recommendation for what should be considered small except to say a int should be able to be stored in place.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 4
    Would have been nice if the max size could have been specified as a template parameter (with a default value of whatever it is now) – Viktor Sehr Jan 26 '17 at 17:24
  • 12
    @NathanOliver Not really. Having a proliferation of `any`s with `any<16>` being a different type from `any<42>` defeats half the purpose of having such vocabulary types. – T.C. Jan 27 '17 at 00:14
  • @ViktorSehr Certainly it can be implemented like that, I did it. If you indicate a way to contact you I can send you my implementation. In my view, `std::any` gives you two different capabilities: 1) type erasure (being able to have an object of any arbitrary type), 2) convertibility from any object type to `any` (changing what you used to hold to something of a different type in the same `any`). If one makes the threshold to not store the value in-place a template argument, you may lose #2, but it is trivial to make a template constructor of other `any`. I don't see the drawback. – TheCppZoo Jan 27 '17 at 05:20
  • @T.C. Hadn't thought about that aspect. It would make it like `std::array` is now. I can see where that would want to be avoided. Maybe they could use a different mechanic to specify what you want to consider small. – NathanOliver Jan 27 '17 at 13:08
  • So we might have a new `std::any_n` besides `std::any` (the latter might even use the former internally), and it would be up to the user which of the disadvantages he'd prefer to have to cope with... – Aconcagua Mar 14 '18 at 13:44
  • @Aconcagua It's possible. Someone would probably have to propose it. – NathanOliver Mar 14 '18 at 13:46
7

If I understand the Boost.Any source code correctly, and from poking at it in a debugger, it does not apply a small object optimization. (Note the unconditional use of new.)

    template<typename ValueType>
    any(const ValueType & value)
      : content(new holder<
            BOOST_DEDUCED_TYPENAME remove_cv<BOOST_DEDUCED_TYPENAME decay<const ValueType>::type>::type
        >(value))
    {
    }
Josh Kelley
  • 56,064
  • 19
  • 146
  • 246
  • @John - I linked to Boost 1.66 or so; see https://github.com/boostorg/any/blob/boost-1.66.0/include/boost/any.hpp – Josh Kelley Aug 24 '21 at 01:27
4

Nathan Oliver's and Josh's Kelley's answers are correct: There is no guarantee, boost does not use the small value optimization.

In more practical cases, https://github.com/llvm-mirror/libcxx/blob/master/include/experimental/any#L129 Shows you libc++ (clang's) gives you 3 void pointers worth of space, (24 bytes if they are 8 bytes wide)

libstdc++ only one pointer: https://gcc.gnu.org/viewcvs/gcc/trunk/libstdc%2B%2B-v3/include/experimental/any?view=markup#l106

Changing any to make the size a template parameter is not difficult, just that you should also make sure there is convertibility between any of different sizes.

IMO, the performance difference is so substantial between small size optimization and heap allocated, according to my benchmarks, and the implementation cost of making it a template so small it will become part of the standard

TheCppZoo
  • 1,219
  • 7
  • 12
  • 2
    I don't think it will become a template in the standard, at least not in the C++17 standard. The standard is already feature complete. – gexicide Jan 27 '17 at 10:56