C++ and the -O3 compilation flag


#1

Oh i know what you are thinking: "Oh god plz no not that topic again they already spam us all the f*cking day on the chat with that i will kill some kittens to relax". And you are right. But why so many people are talking about that ?

Have you tried to code in C++ on codingame ? If not, do a contest in C++ and come back here, we'll wait for you. If you already coded in C++ on codingame, you should already noticed the following points:

  • Calling a function cost you some time. Even with an inline keyword and no copy of value at all
  • Using the STL is the worst idea ever. You kill your performances. Replacing std::max by a simple macro is 3 times faster. x*x is something like 6 or 7 times faster than std:pow(x, 2).
  • Using Object Oriented Programmation is slow even when you avoid copy of values and use only inlined methods
  • Local variables are slow as hell

Why is it so slow despite of all the C++ community saying everywhere on the internet that you MUST use this things ? This are good pratices, this help you to keep your code beautiful and bug-free. Only insane coders avoid this best pratices.

The reason is simple. When you work in the real world with C++, you compile with the -O3 compilation flag (or at least -O2, but i don't know any case where -O3 is not better than -O2). But on Codingame there's no compilation flag (not even a -O0).

When you do a contest, performances are mandatory. Ask anyone in the top 10 of a multiplayer contest and the response will always be the same. You always see C++, C and sometimes C# and Java in the top 10. That's because theses languages just crush others in performances. The only counter example i have is the winner of Back to the code (i believe he coded in Javascript). But @pb4608 could tell you far better than me why Back to the code was a "not so great" contest.

At the moment on codingame if you want performances, you have to follow this rules:

  • Only work with global variables. Forget local variables. Re-use global variables.
  • Never ever ever ever EVER use any function in the std workspace except if you have no choice (you can't really rewrite sqrt everytime you need it)
  • Don't write functions, this is slow, use macros. But you have to know how macros works (or you'll do something slower :smiley: )
  • If you want to use Object Oriented Programmation to have a better code, it will be slow as hell. Why do you think i never coded any Vector class in Coders Strike Back ?
  • You need to use std::sort ? too bad. Just do like @pb4608, rewrite it.

If any entreprise could see my code from a codingame contest they'll just go berserk screaming "This is the worst code i have ever seen and i code in Haskell".

If for example you write in C#, your code will be a little slower than in C++, but you don't have to avoid all the good pratices. You can do a standard code and have all the performances your language can have. C++ is the only language on Codingame where you just can't code like in the real life. You have to learn specifics skills (useless outside of Codingame).

And maybe someone will give the tricks of the pragma (#pragma GCC optimize("Ofast") or #pragma GCC optimize("O3)). But it is NOT the same as the compilation flag. The pragma directive can't do the same optimizations as the -O3 flag. Creating a function still cost you some time when with the compilation flag it cost nothing, STL is still slow, ...

This day, we were talking about that on the webchat. And [CG]VonRickroll said that the response of Codingame was "No, we wont' add this flag, we already said that" (not the accurate words, i can't find it in my history). Well, i know my memory is pretty bad, but i can't remember reading any reasons from Codingame on that.

So i ask the questions here so i think i'll remember the responses:

  • Why do we have no optimisation compilation flag at all ?
  • What are the reasons i must write a horrible code just for Codingame when i can do a normal code in the real life ?
  • Why can't i use any good pratices ?

I think i already know a part of the response: "If we add the -O3 flag, C++ will be too fast and will crush all other languages". But we can respond that:

  • C++ is already crushing other languages. But you have to know how to make a "Codingame's specificaly dirty C++ code".
  • I will be happy to have the computing time of C++ solutions reduced by something like 30% if i had the -O3 compilation flag. Writing dirty code is not one of my habits.

Thanks for reading
A Codingamer disgusted by his own code because of this


[FAQ] (Really) Frequently Asked Questions
#2

If C++ have -O3 flag, I want /optimize in C# too !


#3

Or in the mono / mcs world, -optimize+

This actually used to be the default for mcs, but it changed at some point to a default of -optimize-.

  • danBhentschel

#4

I agree with everything you say, with the caveat that setting -O3 on compilation will not make the discussion go away. C++ optimization is a dark art, often mixed with equal parts science and superstition. There will always be someone who wants to complain about compiler flags until they are given an interface where they can modify the compiler flags in any way they desire.

Oh, and BTW, the discussion won't go away even then, because someone will want version x.xx of g++ so that they can set a new compiler flag that isn't supported in the current version.

That being said, I don't agree at all with the concept (I hope it is a misunderstanding) that it is a good idea to somehow handicap a language to even the playing field. Language selection is a part of the competition, plain and simple. What, are we going to slow down all the languages to be competitive with bash???

If we want to encourage people to learn new languages, we should setup the environment in such a way that it encourages them to learn the language using current industry standard best practices. We should encourage users to select the proper language for a task based on the inherent strengths and weaknesses of the language, not based on artificial or contrived handicaps.

  • danBhentschel

#5

Thank you Magus for having taken the time to clearly explain why we should have the -O3 flag for C++. I totally agree with the reasons you give.
CodingGame should be a place promoting good coding practices and for now, during AI contests, Codingamers coding in C++ are forced to write HORRIBLE code to compensate for the absence of optimization flag.
It is also unfair comparing to other languages regarding code quality perceived by the companies a participant has applied for. For a Top C++ participant, companies see an unreadable code full of bad practices (Magus already gave some of those).
I think Codingamers coding in C++ deserves the right to write elegant and readable code that still executes as fast as possible thanks to the compiler optimization, just as they would do in a company.
CODINGAME, PLEASE FREE THE C++ !


#6

:dagger: This is the worst code i have ever seen and i code in Haskell.

:joy_cat:

Just to clarify: we are talking about compilers and runtime environnements here, not languages.

The only specificity about C++ is that it allows to partially work around theses issues by coding in a certain way that other languages don’t necessarily offer. You could as well think about it as both a power and a curse that C++, as a language, shall fully assume.

So, that doesn’t mean tweaking the compilation flags is a bad idea, just that is a broader problem. A problem I’m not sure CG can ignore easily. After all, the bonus time alloted for the first turn in contests already acknowledges the specificity of some languages runtimes using garbage collectors a certain way.


#7

I'll add a small counterpoint, taking shameless advantage of the fact there's no unlike button around.

C++ is fast enough already compared to the other languages. Slow-as-hell C++ because the coder was weak enough to write it idiomatically still beats the pants off most anything barring proper C.

The solution is not to make C++ faster.
(the solution is not to make it slower either, or to make the others faster, or any middle ground on that axis)

The solution is to stop having competitions where raw performance is the core discriminating factor. But that's going to be hard to swallow for many.


#8

Most of those reasons given are why I code my stuff in C (preferably).

Which that being said... if you want to make an entry without STL and what not to allow C++ to work at any decent... the fact that you pretty much end up submitting a C program is kind of crazy.


#9

The thing is... in order to code C++ without those idioms... you pretty much end up coding in C (Except that one time I managed to code what looked like Perl to the uninitiated in C++, but more accurately looked like Intercal... don't ask... I also lost the bet, I was drunk and sleep deprived).

Since we DO have C as a language to code in for an option... it kind of really doesn't make sense to suggest not coding with those Idioms that C++ makes most of its differences from C with. It would be like coding Obj-C but remove all the code that requires the Obj-C precompiler to convert it into C Code (I know Obj-C hasn't done it like this since the late 1990s... my point still stands).

So... either we work on increasing the time allowed for first turn... or add optimisation. Especially since many of those optimisations are built into the JVM and .Net/Mono virtual machines as part of the JIT.


#10

I think the reason is that we won't be able to provide the line number where the program crashes if we add optimization flags. To my mind we should give to the user the possibility to activate or not the optimization flags, otherwise beginners will lose a feature. And we can't change the optimization flags for submissions only because -O3 can generate bugs you don't see without the optimizations (that would be a nightmare to debug something you only have when you submit).

What do you suggest?


#11

That's the main argument here. The purpose of this website is to promote people's skills to companies.C++ coders should be able to write beautiful code AND have a good ranking.


#12

The absolute best thing would probably be, as you said, to give people the choice of the compilation options, probably in some "advanced" tab. It would also allow people to activate warnings which are currently disabled. However, I don't know how easy it would be for you to add that feature to the website.


#13

The line numbers will still be there as long as there is the debug info compiled in (-g), so this is not a valid argument.


#14

Yep, the only thing that would change for the optimization would be if we wanted to do step by step debugging.

In anycase @MaximeC I guess two options are viable : including at least O2 in the compilation, or leaving the choice of compilation options.

The former is nice but as @player_one said, this won't solve the problem that, after that someone might want to have -march=native or -funroll_loops and blahdiblah. The second option would be actually awesome for every language. But this might provide some security/exploit issues.

I kind of agree with @JBM on his argument towards less "computationally expensive contests". But I also think that, Codingame is somewhat close to real life. You want perfs, you use a fast language, end of story. I love coding in Python, but I would never use it for a HPC code. So in that sense, there is no reason C++ should be penalized because other languages are slower.

And in the worst case you can still add Fortran to have another fast language :wink:


#15

Regarding this topic, here is some more information about how things work.

Basically, CG compiles C++ code using "-O0 -g" compilation options, this means no optimizations, no inlining, nothing at all. Which gives, for std::max, std::vector and std::sort dummy samples, this kind of generated code: https://godbolt.org/g/YOoPB0
As you can see on the assembly side, this is really bad, every single function is called separately, nothing is optimized.

For comparison, here is the code generated when O3 is passed on the command-line instead: https://godbolt.org/g/kLkX62
You see that the compiler inlines a lot of functions, and does aggressive optimization. The call to std::max completely disappears, and the sort implementation is almost fully inlined.

When one uses the #pragma GCC optimize("O3") trick, here's what happens: https://godbolt.org/g/EZdEqB
As you can see, each function gets optimized accordingly to the O3 flag, but they aren't inlined at all. This is why any call to std::max is slower than a macro.

So, what happens here? Well, it looks like the pragma only tells GCC to optimize each function O3-style, but it doesn't activate all the global optimization flags, such as inlining and stuff, and GCC still does this part with the O0-style...

Is it possible to do better with pragmas? Yes. Not as good as command-line O3, but still, quite good: https://godbolt.org/g/syhzgm
By adding another #pragma GCC optimize("inline"), we can override the implicit -fno-inline that comes from O0 optimization, and tell GCC to try inlining the functions that are explicitely marked as inline. Also, the #pragma GCC optimize("omit-frame-pointer") removes the useless stores of the frame pointer, which is enabled by O0 but useless most of the time.

As you can see, for std::max, which is marked as inline in the STL headers, this additional tricks make it as good as if it was compiled with command-line O3.

So why isn't this still not as good as -O3? I'm not sure entirely, but I did notice that the pragma trick for enabling inlining works for functions marked with the inline keyword and for small functions. For functions not marked as such, some of them will not be considered for inlining, although if they would with -O3. This is also the case for every implicitly created functions, such as default constructors and assignment operators. This means that you should define these explicitly and mark them with inline, even if you want the default behavior:

struct bla {
  inline bla() = default;
  inline bla(bla const&) = default;
  inline bla(bla&&) = default;
  inline bla& operator=(bla const&) = default;
  inline bla& operator=(bla&&) = default;
};

Now, using all these tricks, you should have performance almost on par with -O3.


Coders of the Caribbean - Feedback & Strategies
#16

It's been several years since I've done C++ on a regular basis, so my memory / information may be a bit outdated, but aren't optimized stack traces unreliable? I seem to remember that it can sometimes indicate the wrong line numbers if the code has been significantly altered by the optimization process.

  • danBhentschel

#17

I don't know if it is easy, but you could simple add a new language "C++ with -O3". No interface feature to add.


#18

There's a lot of logistics questions around this approach, though. Is this "language" available only for competitions, or is it available on all puzzles too? Is there an achievement associated with it? That would be a bit silly. What would be the upgrade path (for both CG and for users) if this stopgap solution were to eventually be expanded to a system that allowed [partial/full] control over compilation parameters?

Not trying to bash your idea. I think it has merit. Just understand that it's not necessarily as simple as you make it sound.

  • danBhentschel

#19

If the problem is the interface to add, couldn't we simply add a sort of hashbang on the first line of the code, allowing to add stuff on the compilation line ?


#20

Don't forget that bash is available as a language. :wink: