mapbox / variant

C++11/C++14 Variant
BSD 3-Clause "New" or "Revised" License
371 stars 101 forks source link

mapbox::variant is really fast #115

Closed cbeck88 closed 7 years ago

cbeck88 commented 8 years ago

Hi, this is not a "problem" but rather I just wanted to cheer you guys on.

I have been working on my own variant type recently, and I decided to benchmark the visitation speed of variant types. Benchmark framework is pretty stable now, and mapbox::variant looks to be the fastest with both gcc and clang even for quite large numbers of types in the variant.

You guys beat boost::variant, eggs::variant, juice::variant, std::experimental::variant, libcxx dev std::variant, and my variant, sometimes by a wide margin.

Bench results here: https://github.com/cbeck88/strict-variant

artemp commented 8 years ago

👍

tomilov commented 8 years ago

@cbeck88 Will you add compilation time testing for your variant and others? Especially interested multivisitation. Take a look at my variant and benchmarks for it. There are perfect forwarding testing, multivisitation compile time benchmarking and others. https://github.com/tomilov/variant

daniel-j-h commented 8 years ago

The variant implementations you're mentioning are all making different design decisions especially for the controversial case when assignment throws an exception. This can be solved via backup copy (memory allocation), double buffering (inline dbackup copy, twice the size), or some sort of empty / valueless_by_exception state.

These all have different trade-off, and the last time I talked to @artemp about this mapbox/variant didn't care for exceptions during assignment at all.

cbeck88 commented 8 years ago

@daniel-j-h: That's true, but I'm not measuring assignments, I'm only measuring speed of visitation. The main thing that affects it is what TMP strategy you use to convert the runtime-type info (the which() value) back into a static value that you can use for dispatch. Most people did some tricks with an array of function pointers, I did something with a binary tree. mapbox::variant just used naive tail recursion, and it turns out the compilers do the right thing with that. I guess if your variant has an empty state then you might potentially have to throw exceptions when visiting, so maybe that affects the speed of visitation. All these exception-advocates keep telling me that the overhead of that is minimal though :p

In regards to what happens when assignment throws an exception, what I do in my variant is basically, if the type is not no-throw move constructible, then it gets implicitly put in a recursive_wrapper to make it no-throw move constructible. I didn't see that strategy in other variants but I think its a quite nice tradeoff, as I have no space wasted in the variant, no extra copies made of your objects, and I provide strong exception safety. Most programmers already are trying hard to make their objects no-throw move constructible since that puts them on the fast-track when used with std::vector also.

Also, in regards to "not caring" for exceptions, they do a least set the enum to "invalid state" after destroying the contents. So, it's not strong exception safety, but it's what the C++17 std::variant does also, iiuc

cbeck88 commented 8 years ago

@tomilov : I guess that the only thing I'm benchmarking is speed of visitation right now, and what I learned from it is that naive tail-recursion is probably the best way. I can add your variant too though, it's pretty easy to do so.

I don't have anything set up to do compile-time benchmarks, and honestly I'm not too interested in that because my variant targets c++11 and afaik you can't really do a constexpr variant until at least c++14.

@tomilov : It looks like I need to install a new libc++ to compile your variant, I don't want to do that right now, sorry.

artemp commented 7 years ago

closing for now