The Lounge is rated PG. If you're about to post something you wouldn't want your
kid sister to read then don't post it. No flame wars, no abusive conduct, no programming
questions and please don't post ads.
1. The lounge is for the CodeProject community to discuss things of interest to the community, and as a place for the whole community to participate. It is, first and foremost, a respectful meeting and discussion area for those wishing to discuss the life of a Software developer.
The #1 rule is: Be respectful of others, of the site, and of the community as a whole.
2. Technical discussions are welcome, but if you need specific programming question answered please use Quick Answers[^], or to discussion your programming problem in depth use the programming forums[^]. We encourage technical discussion, but this is a general discussion forum, not a programming Q&A forum. Posts will be moved or deleted if they fit better elsewhere.
4. No politics (including enviro-politics[^]), no sex, no religion. This is a community for software development. There are plenty of other sites that are far more appropriate for these discussions. Or if you must, use the Back Room[^] - but enter at your own risk.
5. Nothing Not Safe For Work, nothing you would not want your wife/husband, your girlfriend/boyfriend, your mother or your kid sister seeing on your screen. For those discussions where you wish to be a little more frank, use the Soapbox[^]
6. Any personal attacks, any spam, any advertising, any trolling, or any abuse of the rules will result in your account being removed.
7. Not everyone's first language is English. Be understanding.
Please respect the community and respect each other. We are of many cultures so remember that. Don't assume others understand you are joking, don't belittle anyone for taking offense or being thin skinned.
We are a community for software developers. Leave the egos at the door.
Magic doesn't exist by definition, but there are also real reasons why compilers do not rise to meet the naive expectations that are nevertheless often repeated as a kind of software engineering meme. Some people think memes are true and maybe some wishful thinking plays a roll (I get it, it would be great if the compiler was magic), and will just think I'm talking sh*t when I give the short version, so here's a longer one.
There is way too much to cover, so for now I'll just concentrate on one thing, why are compilers not godly at code generation. They are pretty good nowadays, but their mythical status is undeserved.
A fairly fundamental problem (for both compilers and novice programmers, but programmers can learn) is that the cost model is wrong, so when it's tiling its internal representation with pieces of machine code, it's not modeling reality accurately enough.
As far as I know, every reasonable code generation technique, even advanced ones want the cost to be a scalar.
But it isn't a scalar, not in an accurate model of reality anyway, not since the end of what I'll call "simple architectures" (circularly defined as those architectures where the cost of an instruction is the number of cycles it takes and no other considerations exist).
What does reality look like? Let's look at the code below. It doesn't really matter what it actually does, I'm just going analyze the cost (for Haswell) to show a bit of what's involved.
This is a fairly interesting loop because it has a non-trivial loop carried dependency, which I have drawn here (two iterations shown, arrows are in the direction of the dependency, data flow is from bottom to top "against" the arrows). Just adding the latencies on the critical path (imul3, add3, add4, shrd2) or (mul2, add4, shrd2), either way gives 8, but it actually costs 9 cycles per iteration: imul3 and mul2 cannot be executed in the same cycle (both need p1), one of them has to wait a cycle and either way that holds everything up by a cycle.
There is a bunch of other code in this loop, but it "fits in the gaps". In general, you do have to care about the other code, especially in typical throughput-limited loops.
This is tricky enough by hand, now imagine implementing that in a compiler. What does the model even look like? Certainly not like a "just combine everything into one scalar"-cost that you can simply add, that's not even close. A vector "pressure per port" seems like an obvious model for loops with only a trivial loop-carried dependency, but even that is really tricky: many instructions can dynamically go to a port with a low pressure (eg p0156 means it can go to ports 0, 1, 5 or 6), modeling that as 1/4 pressure to each port only works if there are no instructions that must go to a certain port, but there usually are. You could distribute those instructions across the ports like a CPU would, but only if you know the context, so now you have a cost that does not just depend on the tile that you're looking at but also all other tiles (which you may not even have chosen yet!).
Reality is a mess, and compilers just don't model it (though they could). Not out of laziness, implementing a realistic model means you can't use the old DP tiling algorithm (which for DAGs isn't optimal anyway, but with some tweaks you can get close) because you don't have optimal substructure: the cost of a sub-tiling depends on the context in which it appears, the best tiling may not consist of locally-optimal sub-tilings.
To give a fairly abstract example of that, suppose you have parts A and B, part A can be tiled either such that it has 2 µops going to port 1, or such that it has 1 µops to port 0 and 3 to port 5. Which is better? It depends: if part B needs to send 2 µops to port 1 then combining it with the "locally better" first option gives port 1 a pressure of 4, which (if there is no other context and we're talking about throughput) is worse than combining it with the second option where the worst port pressure would be 3. With more context, the decision can flip again.
Often heard: "Compilers know better what is fast is what isn't than you do."
Well you can fix that, start here.
An other big problem is that a lot is set in stone before code generation. Whether a certain optimization should be applied depends on how it actually works out during code generation, but compilers are too linear for that - they optimize their IR, then do code generation. If they make a choice that works out badly, too bad.
Ideally (from a quality perspective) any choice should have its consequences computed by running all possible versions all the way through code generation. Choosing based on anything else is essentially a guess, though there are "obvious cases". But it would be way too slow, since many of the decisions stack to an exponential number of versions that would be tried. Not all decisions affect each other of course, but it's bad enough.
It is really the opposite of the workflow of a human, if I may be so bold as to speak for an entire species, we're all about trying different approaches and seeing what works out.
The higher level problems are even worse, maybe more on that some other time..
I'd agree - compilers are pretty good, but they still don't come close to an experienced human who knows what he is doing with a specific machine code / assembler. Part of that is that the language being compiled enforces specific structure on the program being written, which may not be an ideal match for the task being coded: an example I had was where I needed to output 128 bit data serially with a clock bit - The compiler generated code was slow as heck because it just didn't know what exactly I was trying to do, and there was no way to tell it. In assembler, it was two machine instructions per bit and an order of magnitude faster (and the clock was symmetric as well, unlike the compiler version).
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
In the beginning there were only ones and zeroes, then came Assembly language. Unfortunately, people did not understand what they saw.
An angry mob came forth carrying pitchforks and shouted: "Aye, dark wizards! Keep yer magic tomes of olde to yerself! We dun want yer magic here, lest ye curse us all to heck! Now let us call upon the Witchfinder General, that she may release us from evil!"
And thus came forward Grace Hopper, who wrote the first compiler, hiding the runes of the computer which people did not understand. And people could use higher level languages and they did not look back. Yet, compilers have since been known to contain dark magic, but a necessary evil and those who dare open up these Pandora boxes are known as dark wizards.
And how do unspeakable interpreters from Mordor fit into that picture?
They don't fit... they just force place for them fighting around
If something has a solution... Why do we have to worry about?. If it has no solution... For what reason do we have to worry about? Help me to understand what I'm saying, and I'll explain it better to you Rating helpful answers is nice, but saying thanks can be even nicer.
Having spent a lot of time developing an online HTML Editor for our intranet I now appreciate IE11 a lot more. They finally got it right. It is almost completely standards compliant and everything just works the way it should, even more so than either Chrome, Firefox or Safari, which I also have to support.
...and then, Edge.
As soon as Microsoft finally gets something right, full featured and completely working, they have to mess it up with a new, "improved" version.
IE11 was fine, Edge isn't. Windows 7 was fine, everything since isn't. VS2015 was fine, VS2017 isn't. Office... well, you see where I am going with is.
- I would love to change the world, but they won’t give me the source code.