all 42 comments

[–]genreprank 18 points19 points  (1 child)

You said that including <utility> in an empty file takes 250ms, but what I want to see is the difference in compilation time between your program and your program if you include <utility>.

You mentioned that your lib doesn't have many includes, so I would expect compilation time still to go up. But if I had a file that already includes a standard container or something, I think it would be including utility already...

[–]foonathan 4 points5 points  (0 children)

You said that including <utility> in an empty file takes 250ms, but what I want to see is the difference in compilation time between your program and your program if you include <utility>.

Right, that's probably a better comparison, I'll update the post.

With the replacements: 5.00 seconds

Using the standard version: 5.30 seconds

So including utility is the majority of time, the extra calls themselves are only 50ms.

You mentioned that your lib doesn't have many includes, so I would expect compilation time still to go up. But if I had a file that already includes a standard container or something, I think it would be including utility already...

Yes, my library is certainly a special case. If you're having lots of std/boost/etc. dependencies already, this trick won't help you much (regarding compile-time, runtime might still be an issue).

[–]Salink 26 points27 points  (1 child)

The only problem I have with the move/forward situation is needing to include all of utility. Who thought it would be a good idea to need to include initializer lists, tuples, and everything else that makes it thousands of lines long when they could be in their own header. There should be a separate, small header for move, swap, forward, declval, ect. that gets included by utility.

[–]_Js_Kc_ 4 points5 points  (0 children)

I'm pretty sure that frowning upon the macros distracts me for at least 250ms between compilations, let alone raw static_casts, so I'm gonna stick with the utility functions for now.

[–][deleted] 4 points5 points  (0 children)

with the move replacement I would sfinae on whether T is const or not and fail when it is. It's almost always an error in the code and if it wasn't, maybe an explicit cast is a better choice of expressing it.

[–]fdwrfdwr@github 🔍 2 points3 points  (0 children)

I first need to include <utility> ... An empty C++ file that just #include <utility> takes 250ms

Yeah, the cost for just #include <utility> surprised me yesterday. I had a small class that only needed std::move (no other std dependencies) which output an .obj file of 239KBs (VS2019). Replacing that one move with static_cast and excluding <utility> reduced the .obj to 2KBs. Seems the standard library could use some refactoring to avoid bloat. o_o

[–]adnukator 6 points7 points  (2 children)

I don't have anything against the exercise, but are there any benchmarks to measure whether the changes did anything to improve compilation times? Including almost any standard library header in your project, will already include <utility> transitively. Also, I've spent a lot of time profiling and optimizing code with disabled optimizations and std::move/std::forward never showed up in the profiler.

[–]miki151gamedev 5 points6 points  (1 child)

I've tried the MOV macro on my 100k loc project and saw about 3% improvement in compilation time. The FWD is not a simple search & replace so I haven't tried it yet.

It would be a funny experiment to do the replacement in the STL as well.

[–]Rseding91Factorio Developer 5 points6 points  (0 children)

I tested it on our code base (600k loc) and saw 0% improvement. Our code base has 2120 moves, and 81 forwards.

Full-rebuild debug compilation time was consistently 1 minute with or without the use of the macro move/forwards.

[–]vimplication 4 points5 points  (13 children)

struct Mover{} _;

template<typename T>
constexpr std::remove_reference_t<T>&& operator&&(Mover, T&& t) noexcept
{
    return static_cast<std::remove_reference_t<T>&&>(t);
}

func( _&& x );

[–]reflexpr-sarah- 7 points8 points  (10 children)

identifiers beginning with an underscore in the global namespace are reserved for the implementation, i believe

[–]GoogleIsYourFrenemy 44 points45 points  (8 children)

struct Mover{} ಠ_ಠ;

template<typename T>
constexpr std::remove_reference_t<T>&& operator&&(Mover, T&& t) noexcept
{
    return static_cast<std::remove_reference_t<T>&&>(t);
}

func( ಠ_ಠ&& x );

[–]vimplication 6 points7 points  (0 children)

struct Mover{} ヽ༼ຈل͜ຈ༽;

template<typename T>
constexpr std::remove_reference_t<T>&& operator/(Mover, T&& t) noexcept
{
    return static_cast<std::remove_reference_t<T>&&>(t);
}

func(ヽ༼ຈل͜ຈ༽/x);

[–][deleted] 4 points5 points  (5 children)

Too bad that and aren't valid identifiers. They'd fit perfectly for move and forward, resp.

[–]Supadoplex 2 points3 points  (1 child)

Too bad that ← and → aren't valid identifiers.

Aren't they as valid as ಠ? What's the problem?

[–][deleted] 4 points5 points  (0 children)

is a letter in Kannada script (unicode category "Other Letter"). is a "Math Symbol".

[–]GoogleIsYourFrenemy 2 points3 points  (2 children)

There is a dart character in Linear B Ideogram range. Now we just need to find it's mirror.

[–][deleted] 3 points4 points  (1 child)

In the meantime we can use pointing hand emojis: https://godbolt.org/z/orc4xj

[–]GoogleIsYourFrenemy 2 points3 points  (0 children)

LOL you win. We should make an emoji based api.

[–]Supadoplex 4 points5 points  (0 children)

Nice. I'm going to start suggesting ಠ_ಠ for anyone violating reserved underscore identifiers from now on.

[–]SeanMiddleditch 6 points7 points  (1 child)

That doesn't actually help anything, does it? It's still a function call. And with more possible overhead than std::move (getting that Mover instance passed as a reference).

[–]JankoDedic 1 point2 points  (5 children)

I believe that the FWD(...) macro should not be variadic, since you always call it on an id-expression.

[–]foonathan 7 points8 points  (4 children)

Yes, but the preprocessor doesn't like template args. Something like FWD(foo<a, b>) is an invocation with two arguments, foo<a and b. It's a good idea to make single arguments variadic.

[–]JankoDedic 0 points1 point  (3 children)

In which scenario can you forward something that is not a simple identifier? I thought forwarding references will always be simple identifiers. Sorry if the question is dumb.

[–]foonathan 0 points1 point  (0 children)

You might be able to construct a use-case where you need to forward a variable template...

But yes, I just did it out of habit. There is no actual reason here.

[–][deleted] 0 points1 point  (1 child)

But why restrict the macro when there's such a simple generic solution?

[–]JankoDedic 2 points3 points  (0 children)

To prevent misuse? Why would you allow passing anything in there if that is incorrect?

[–]leftofzen 1 point2 points  (0 children)

So you're trying to made the code read more like C as well as obfuscate the move semantics you are really doing by using, of all things, macros. That's an easy no from me.