16

Now that std::experimental::optional has been accepted (or is about to be accepted), I wonder what is the overhead and the consequences on the assembly generated when the inner value is get by the following operators :

->
*
value
value_or

compared to the case without std::optional. It could be particularly important for computationaly intensive programs.

For example, what would be order of magnitude of the overhead on operations on a std::vector<std::experimental::optional<double>> compared to a std::vector<double> ?

manlio
  • 18,345
  • 14
  • 76
  • 126
Vincent
  • 57,703
  • 61
  • 205
  • 388
  • 6
    You need a more precise testcase to be able to judge. Overhead can go from negligible to a factor 10 (prevents vectorization and other optimizations). But then you won't use them the same, one has extra functionality, so it is comparing apples and oranges. – Marc Glisse May 07 '14 at 16:26

2 Answers2

16

-> and * ought to have zero overhead.
value and value_or ought to have the overhead of one branch: if(active)
Also, copy/move constructor, copy/move assignment, swap, emplace, operator==, operator<, and the destructor ought to also have the overhead of one branch.
However, one banch of overhead is so small it probably can't even be measured. Seriously, write pretty code, and don't worry about the performance here. Odds are making the code pretty will result in it running faster than if you tried to make it fast. Counter-intuitive, but do it anyway.

There are definitely cases where the overhead becomes noticible, for instance sorting a large number of optionals. In these cases, there's four situations,
(A) all the optionals known to be empty ahead of time, in which case, why sort?
(B) Some optionals may or may not be active, in which case the overhead is required and there is no better way.
(C) All optionals are known to have values ahead of time and you don't need the sorted-data in place, in which case, use the zero overhead operators to make a copy of the data where the copy is using the raw type instead of optional, and sort that.
(D) All optionals are known to have values ahead of time, but you need the sorted data in-place. In this case, optional is adding unnecessary overhead, and the easiest way to work around it is to do step C, and then use the no-overhead operators to move the data back.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • 6
    *"However, one banch of overhead is so small it probably can't even be measured"* Huh? In a tight loop, it can certainly have an impact on performance. http://stackoverflow.com/q/11227809/420683 – dyp May 07 '14 at 17:31
  • E.g. compare http://coliru.stacked-crooked.com/a/33db47995d378111 to `T == int`. (This shouldn't even be a branch-misprediction effect, I guess.) – dyp May 07 '14 at 17:38
  • 1
    @dyp: Yes, it does have an impact on performance, but in most real code, the impact is lost in the noise. In contrived cases it can be made _very_ clear (as the examples you show), but I believe the amount of real world code that would be seriously affected by this is probably minimal. On the other hand, I believe micro-optimizations _do_ have serious performance impacts on real-world code. – Mooing Duck May 07 '14 at 17:39
  • I agree that it probably heavily depends on the actual program and the context (that's pretty much what Marc Glisse is saying, I guess). But the OP asked for a `std::vector`, so I think sorting a large vector of `int`s or `double`s could be a reasonable scenario. – dyp May 07 '14 at 17:45
  • For an unchecked version, instead of rewriting a custom sort, it is simpler to wrap iterators. – Marc Glisse May 07 '14 at 20:51
  • @MarcGlisse: Several swaps move elements to the stack while they work, so you'd have to coax the sort to sort something besides optionals. You could sort iterators/pointers instead, but then it's slightly tricky to rearrange the originals into shape. I think the easiest way would be to copy the data to a `vector`, sort that, then copy the values back using the no-overhead operators. – Mooing Duck May 07 '14 at 21:12
  • @MooingDuck: "don't worry about the performance here" : I work in the domain of high performance computing astrophysical simulations, and I worry, because when you use 50 million of computing hours per year, you need to know whether your code is efficient or not... – Vincent May 15 '14 at 13:34
  • @Vincent: I say don't worry about performance because 99.9% of the time the overhead of `std::optional` is the difference between 50 million hours of computing and 50.001 million hours of computing. – Mooing Duck May 15 '14 at 16:44
16

Besides the other answer, you should also consider that std::optional requires additional memory.

Often it's not just an extra byte, but (at least for "small" types) a 2x space overhead due to padding .

Maybe RAM isn't a problem but that also means fewer values available in the cache.

A sentinel value, if specific knowledge allows to use it, could be a better choice (probably in the form of markable to keep type safety).

An interesting reading is: Boost optional - Performance considerations

manlio
  • 18,345
  • 14
  • 76
  • 126