3

I'm working on a large performance-critical project that is very branch heavy. In the process of designing algorithms for this product, my employer often reminds me to write code that is more "human logical", or written in a manner that more closely aligns with the way we logically think.

While this makes sense to me from a few different perspectives (e.g. ease of understanding/remembering, code maintenance, etc.), I'm also wondering whether this approach could also ever be expected to lead to a more optimized compiled output.

Could this be the case due to the fact that compilers are written by humans, and optimizers are often designed to recognize familiar code blocks?

I would love to hear some thoughts on why this could/not be the case.

Theodus
  • 31
  • 1
  • If you use `class`es/OO in C++, it's much more understandable (to me) than the "C version" of the code. But it's just a tiny bit slower too. – Mateen Ulhaq Jul 07 '11 at 02:38
  • @muntoo: That is an urban myth. Classes do not make code _any_ slower, and properly written C++ is usually as fast or faster than C. Excessive and unsuited use of OO on large datasets may indeed result in slower code due to bad cache behaviour, but that is a user error, not a property of OO. Enabling RTTI or exceptions will also make C++ slower, but you cannot compare apples and oranges. Implementing an equivalent functionality otherwise would have the same overhead in every other language too. Similar is true for virtual functions (which are no performance issue at all, if used right). – Damon Jul 07 '11 at 11:47
  • There are some odds that your employer is talking about the way *he* thinks. Not so sure that's a good match. Optimizers go for the most likeliest benefit. Which is found in code that's written according to widely used standards and practices. That will also be code that's easily recognized, regardless if it matches human thought patterns. – Hans Passant Jul 07 '11 at 14:42
  • @Damon: at the machine code level some classes (such as for linked lists) produce numerous function calls when stepping through the members of a list. AFAIK linked lists are supposed to be a cornerstone of OO. If you skip them you might as well write the code in C which WILL BE faster. On the other hand you'll lose some of the flexibility inherent in the lists. Tradeoffs tradeoffs all the time tradeoffs. Also a poor C programmer will write slower code than a good C++ programmer. What will be fast and what will be slow depends on a lot of things which you don't seem to appreciate. – Olof Forshell Jul 07 '11 at 20:29
  • @Olof Forshell: Comparing a linked list with something that is not a linked list (e.g. an array) is silly. If you compare a vector with an array, you will see that accessing its elements or iterating over it produces exactly the same machine instructions as C pointer code (in non-debug build). With the difference that writing the code is much easier, takes less time, in debug mode you have bounds checking, and you're not going to write past the end of the array because you got the length wrong or forgot to realloc. At many occasions, I've seen STL outperform "hand-tuned" unreadable code. – Damon Jul 07 '11 at 22:09
  • @Damon: using classes is the way you do things in C++, in C you don't. C++ without classes is like "Dry Martini without the egg" (as Gomez Addams in the original B&W series once said). Classes cause extra code to be generated and extra code NEVER executes without a penalty. The C++ compiler will compile C code as well as the C compiler because they (usually) share the same source code. As to your other statements about writing programs in C++ and unreadable C code I believe you are voicing opinions but peddling them as truths. – Olof Forshell Jul 10 '11 at 14:38
  • @Olof Forshell: Create a vector on a builtin type and iterate over the elements, doing some operation on them. Do the same with C array/pointer/whatever, and compare the disassembly. You will see that this is not opinion-peddled-as-truth, but truth. If you want to keep insisting that classes cause extra code, that's of course fine with me. Classes _can_ generate extra code and _will_ generate slower code if people don't use them properly. This is a regular argument in opinion-based language wars and unqualified bencharks ("Java is faster than C++"), but that's about it. – Damon Jul 10 '11 at 15:20
  • The cases where classes do cause extra overhead are well-known (or should be by the people using them), and in those cases, the overhead is well justified too. It is usually much more important today to write comprehensive, bug-free, maintainable, and reusable code rather than save 3 clock cycles. With a modern compiler, even at a high level of abstraction, the measurable difference is exactly zero (and again, you have to use an array-like container if you want array behaviour, instead of complaining that a list performs badly in that case, lists have other cases where they perform well). – Damon Jul 10 '11 at 15:24

2 Answers2

1

Consider two different kinds of code, library code and application code. Library code (like a string class library) is likely to own the program counter a lot of the time, like this:

while(some test){
  massage some data, while seldom calling sub-functions
}

That kind of code will benefit from compiler optimization. (So to answer your question, people write benchmark functions like this, and the compiler-writers use those as test cases.)

On the other hand, application code tends to look like this:

if (some test){
  do a bunch of things, including many function calls
} else if (some other test){
  do a bunch of things, including many function calls
} else {
  do a bunch of things, including many function calls
}

In this case, the time you save by branch prediction or cycle-shaving might be 1 time unit, say, while the do a bunch of things... might spend from 10^2 to 10^8 time units, with or without I/O. So the benefit of compiler optimization of this code tends to be completely lost in the noise.

That's not to say it can't be optimized. It's just that the compiler can't do it - it's your job.

If you want to make the latter kind of code run fast, the best way is to find out which lines of code are on the call stack a high percent of time, and if possible, finding a way to avoid doing them. (Here's an example of a 43x speedup.)

Community
  • 1
  • 1
Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135
0

What is "human logical" probably varies from human to human.

For instance, if I am a newbie performing tasks according to written instructions I will (usually), over time, learn some tasks by heart whereas for others I will return to the instructions simply because the tasks are not performed often enough/are too boring or both. Others in the same situation may or may not function similiarly and it is not certain that the tasks they'll learn will be the ones I learn.

For programming it works similarly. Some may construct a loop in one manner and perform a test inside it for the sake of readability while I might do the test outside for performance reasons. What is more wrong and what is more right?

There is a widespread belief that compilers will optimize anything. This is true but as I've written (drastically) in another post, GIGO (Garbage In = Garbage Out) applies. Compilers don't operate in a vacuum: given a set of rules they'll perform safe optimizations on source code to the extent of their (the compilers') constructors' imagination and competence in code optimizations. Bloat source code will become optimized bloat machine code. In the same manner lean and mean source code will become optimized lean and mean machine code. In critical places it is possible to feed the compiler source code that it "feels" (YES! they do have personalities) absolutely comfortable in optimizing and the resulting machine code will fly.

We've all experienced poorly performing software. If we're lucky we've experienced software that performs incredibly well. One developer can learn to write a piece of code that performs well in the same amount of time that another writes code that performs poorly.

Olof Forshell
  • 3,169
  • 22
  • 28