Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is using std::optional<int> as efficient as using int?

I have a quad-/octree data structure. Im storing the children indexes/ptrs of a cell in an array. Each position in the array represents the location of a child with respect to its parent, e.g. in 2D:

// _____________
// |     |     |
// |  2  |  3  |
// |_____|_____|
// |     |     |
// |  0  |  1  |
// |_____|_____|
// for each cell, 4 children are always stored in row-major order
std::vector<std::array<Integer,4>> children;

I know that the max number of children is a subset of the values that an Integer type can represent. Thus I can identify if a cell is missing a child by using a ''magic'' value like -1 for Integer = int, or std::numeric_limits<unsigned>::max() for Integer = unsigned. This is something that std::optional<Integer> cannot assume.

As far as I understood, this usage of magic values is one of the raison d'être of std::optional. Still, I'm worried about the performance of std::vector<std::optional<int>> in inner loops.

So,

  • Will the performance of std::vector<std::optional<int>> be worse than that of std::vector<int>? (I'm already doing the comparison for "non-existent" value).

  • Or, can the implementation of std::optional be optimized to offer the same performance as a raw int? And how?

Mixing std::optional in the return type of my functions and magic values in my data structure sounds like a very bad idea. I prefer to be consistent and either use one or the other (at least within the same context). Although I could overload the function that performs the comparison with the magic number:

template<T> bool is_valid(const T& t) { 
  return /* comparison with magic value for t */; 
}

for optional types.

like image 846
gnzlbg Avatar asked Jun 26 '13 15:06

gnzlbg


People also ask

How efficient is std :: optional?

As a conclusion, std::optional is as efficient as a custom type to represent an optional integer value. Don't implement your own type, simply use the standard type.

What is std :: optional used for?

We use std::optional to make our code more expressive. std::optional contains the object within itself, depending on where it is stored (stack/data/heap) std::optional makes a copy of the contained object.

Does STD optional allocate?

What's more, std::optional doesn't need to allocate any memory on the free store. std::optional is a part of C++ vocabulary types along with std::any , std::variant and std::string_view .

Is std :: optional a pointer?

Thus, an optional object models an object, not a pointer, even though operator*() and operator->() are defined. When an object of type optional<T> is contextually converted to bool , the conversion returns true if the object contains a value and false if it does not contain a value.


2 Answers

std::optional is going to require additional storage and fit fewer values into cache (it appears you already know the reason for this).

I don't think it's wrong to have a different value stored internally in your data structure from the one exposed by the public API, as long as the internal representation is completely hidden from users.

Furthermore, I suggest you isolate the magic number into a single pair of inline conversion functions.

The compiler should help you remember to use the conversion functions consistently, by generating type errors if you forget. You might even use a thin struct wrapper for an int in your internal data structure, to ensure that no implicit conversion exists (or define a user-defined conversion).

class CompressedOptionalUInt
{
    static const unsigned SENTINEL_MISSING = std::numeric_limits<unsigned>::max();
    unsigned value;

public:
    CompressedOptionalUInt(std::optional<unsigned> val) : value(!val? SENTINEL_MISSING: *val) {}
    operator std::optional<unsigned>() const { ... }
};

and then use std::array<CompressedOptionalUInt>.

Making that into a template, with just the sentinel needing to be defined for each type, should be pretty straightforward.

like image 120
Ben Voigt Avatar answered Oct 21 '22 12:10

Ben Voigt


No, it's not as efficient. As you can see from the reference implementation it has to store, update and check an extra value.

like image 37
Jack Aidley Avatar answered Oct 21 '22 12:10

Jack Aidley