Obviously it is not possible to compare exact outputs without using the same target devices. But it would seem that you are getting roughly the same kind of optimisations - but with a good deal more manual work. Still, it is nice to see that it is possible to do this in Forth too.
It is likely that this is standard startup code. In my tests, I omitted that - but by default you'll get code to clear the bss, copy initialised data, etc. That is all fixed overhead in a normal program, and any serious assembly code will need something similar. (On my testing, that common code was around 80 bytes, not 312 bytes, but details will vary by compiler.)
If compiler flags "optimise away" your code, you don't understand how to program in C.
If you try to write something in English, and you spell it incorrectly, whose fault is it? If you try to write something in assembly but get the instructions wrong, whose fault is it?
If you try to write something in C, but do not know how to do so correctly, then it is /not/ the compiler's fault - it is /your/ fault.
Yes, it is /your/ fault. And if your tools don't spot such mistakes automatically, it is /your/ fault for not understanding how to use your tools properly. Just like when you hit your thumb with a hammer - it is not the fault of the hammer or the nail.
If you want to use a tool, learn how to do so. Compilers are not mysterious or malicious - it is just that some users are incompetent.
On that link what an annoying writing style, same paragraph over an over making minor changes to the paragraph sometimes repeating minor variations of a paragraph ten times.
In C++ you shouldn't even be using the "==" operator on the basic literal types most of the time, anyway. It means your types aren't strong enough. You shouldn't ever be able to have -5000 pieces of gold or be 350 years old.
Instead you have classes which encapsulate and sanitize your user data. In C++1x:
auto player_1_gold = player_gold_t{500}; //player1 has 500 gold auto player_2_gold = player_gold_t{1000}; //player2 has 1000 gold
So this works (assuming appropriately overloaded "==" operator):
player_1_gold == player_2_gold ? ...etc. //OK
But you define your copy constructor and user defined integer-conversion operator "explicit" to make stuff like this illegal:
if (player_1_gold = 1000) //Compiler error, can't assign "500 int" to a "player_gold_t"
if (player_1_gold == 500) //Compiler error, No implicit comparison possible between "500 int" and "player_gold_t"
if (player_1_gold = player_2_gold) //Compiler error, no implicit conversion from "player_gold_t" to "bool"
if (player_1_gold == static_cast(500)) //OK
void foo(unsigned int bar) {...
foo(player_1_gold); //Compiler error, no implicit conversion from player_gold_t to "unsigned int"
auto player_3_gold = player_2_gold //Compiler error, Copy constructor marked "explicit", direct initialization of "player_3_gold" from "player_2_gold" not possible
auto player_3_gold{player_2_gold}; //OK, player_3_gold initialized from player_2_gold's value
...
And this should all compile down to have no overhead as compared with the same operations written in straight C.
That is to say, if you get it wrong nowadays with modern C++ the "donkey" is _you_. It's the programmer's job to write sane code, not for the compiler to divine what it thinks you mean.
That was silly, just profile the code to see where the bottlenecks are and rewrite those parts. No point in rewriting code for efficiency that the CPU is only spending 1% of its time executing (usually 90% of the codebase)
It doesn't. What you mean is: By (often wrongly) assuming that C programs don't access data through pointers of other types, adversarial C compilers save themselves the trouble of trying to do proper alias analysis. But of course, the result is often wrong, because the assumption is often wrong.
The Forth *programmer* gets rid of unneeded loads and stores by not writing them.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2017: http://euro.theforth.net/
Yeah, right. Too bad you were not consulted when they made the decision, you would have saved the company a lot of time and money. Although, thinking about it, if, say, a 30M-cycle run takes 100,000s on the first simulator, and 1,000s of that is spent in 90% of the code, and you manage to speed up the other 10% by a factor of 99, the result will still take 2,000s for the run, while the new simulator takes 10s.
Anyway, with the "crazy contorted optimizations that the compiler puts in", such that "C++ will likely beat handwritten assembler for any sizeable program", the C++ compiler should have optimized the original simulator to be as fast as the final simulator all by itself. It didn't, so the"crazy contorted optimizations" may be crazy and contorted, but are not up to the task of competing with source-level optimizations.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2017: http://euro.theforth.net/
"Undefined behavior" which is highly application-specific. Anything from no ill effect, to incorrect output (either immediately or at some later time), to terrible data corruption, to a quick crash/segfault.
In extreme cases, things like "destruction of hardware" and "loss of life" can occur.
I once took advantage of a compiler bug in a Univac FORTRAN system, to demonostrate to my doubting FORTRAN instructor that the computer believed that two plus two equals six. I incremented the value of a parameter which was passed into a FORTRAN function (and FORTRAN of the era always passed all parameters by reference, not by value). Since I'd passed a constant "2", and since the compiler hadn't bothered either to memory-protect the constant or provide an emphermeral copy... foom.
The "all constants are variable" rule took hold. :-)
It's always possible to write inefficient algorithms in any compiled language that will compile to well-optimized (for the algorithm) but inefficient (for the application) code.
For the comparison to make any sense you have to make the assumption that the engineer is not doing bonehead things like say making thousands of superfluous copies of a large object per second when a simple move would suffice. Unfortunately in general that's easier to do in a language with OOP-features like C++ than straight asm. That particular problem has become less of an issue in the later versions of the standard with move constructors and copy elision/RVO.
I once had a subroutine that was passed a boolean (LOGICAL as they call it) , that I set to FALSE after inspection. When I inadvertantly called this with a TRUE literal, TRUE was turned into FALSE for all of FORTRAN and hell broke loose.
At the time it was considered a bug in the program not in the compiler.
Anyway the specification of C++ and its standard library is such that no single expert is master of it all. Regularly conflicts are detected between remote parts of it. Bottomline, not a language for mission critical stuff or cancer radiation equipment.
There are more important things than a little speed.
Groetjes Albert
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst
It's used in that stuff all the time, though. The list of undefined/implementation-defined behavior is not infinite.
And in 2k17 if one is consistently running into issues with undefined behavior it probably means You're Doing It Wrong, i.e. you're not really writing C++, you're trying to write "C with Classes." There's all kinds of stuff in C++1x to support backward compatibility that one should never actually use in new code.
i.e. memset is obsolete. memcpy is obsolete. all those "mem" functions are obsolete. Doing any kind of direct byte-for-byte copy of structures is pretty much obsolete.
C-style unions are pretty much obsolete, you use a std::variant type. C-style arrays are obsolete, I never use them. If you need a plain ol' const array you use a std::array. If you need a list of stuff you can modify you use a std::vector. Raw pointers are pretty much obsolete except for when you need to make a user-defined conversion to pass something to a C API call that can only accept pointers. "Naked" "new" and "delete" are obsolete unless you're writing a custom allocator.
"For" loops that increment counters are obsolete...etc.
One of the driving ideas of C++ is to provide useful abstractions at essentially zero cost. Evidently it wasn't used that way in that first implementation. Was it written in C++1x (i.e. C++11 or later)? C++11 made it much easier to do certain important things efficently, by introducing move constructors.
It could be that the first implementation was intended as a prototype. I probably would have done it in Python or Haskell, so it would have been even slower, but allowed easier instrumentation and exploration.
Well, I'm thinking partly of the restrict keyword introduced in C99, which explicitly tells the compiler that there's no alias.
That's true, accessing data through pointers of multiple types is not allowed in C, so it's unsurprising that programmers who make wrong assumptions to the contrary can get wrong results. Wrong assumptions are the cause of plenty of bugs no matter what language is used.
That requires close manual control over large stretches of code, that a real compiler automates.
ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here.
All logos and trade names are the property of their respective owners.