2

20 years ago, there was (almost) no any compilers optimizations. So, we started to use some hacks, such as:

  1. Use pointers, not array indexes.
  2. Don't use small functions (such as swap()), use macros or write the code directly.

Today, we have complex compiler optimization. Array indexes and pointer are same. If we use -O3 (I know, it's dangerous), compiler will remove all functions except main().

So, the small hacks in the old books (Programming Pearls, The C Programming Language) are useless today? They are just make the code more unreadable?

AstroCB
  • 12,337
  • 20
  • 57
  • 73
比尔盖子
  • 2,693
  • 5
  • 37
  • 53
  • 4
    "20 years ago, there was (almost) no any compilers optimizations" - really? – Joe Nov 29 '12 at 13:19
  • 1
    `the small hacks in the old books are useless today?` It's hard to tell how today's compiler treats/optimizes each of those hacks. Perhaps, you can narrow down to a specific example? – P.P Nov 29 '12 at 13:25
  • Question is probably better suited to programmers.SE ? – Paul R Nov 29 '12 at 14:25

4 Answers4

6

Programming Pearls is about optimisation at the algorithm level, not at the code level, so it's still highly relevant today.

Code micro-optimisations are another story though, and many of the old tricks are now either redundant or even harmful. There are still important techniques that can be applied to performance-critical code today, but these also may become redundant/harmful at some point in the future. You need to keep up-to-date with advances in CPU micro-architecture and compiler technology and use only what's appropriate (and only when absolutely needed of course - premature optimisation being the root of all evil.)

Paul R
  • 208,748
  • 37
  • 389
  • 560
  • 1
    I'll add to this that while `many of the old tricks are now either redundant or even harmful` is true, many of the "old tricks" are still in use in large code bases and it's important to know and understand what they are doing when viewing other's code. – Mike Nov 29 '12 at 13:40
  • @Mike: good point. I should probably also add that *some* old tricks are still good even today. – Paul R Nov 29 '12 at 13:45
  • I would say that _the intention_ of the book is highly relevant. The actual book is a veritable orgy in poorly-written code and should, in my own arrogant opinion, be burned on sight. But book opinions is of course a subjective topic. This site has a [book FAQ](http://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list) that may or may not list the most relevant C programming books. – Lundin Nov 29 '12 at 14:10
  • @PaulR It wasn't really anonymous, it was me who put a -1 for the statement of that book being practical. I'll be happy to change the downvote to upvote if you remove that part, or at least clarify that it is your own personal, subjective opinion. – Lundin Nov 29 '12 at 15:18
  • OK - noted - I've removed the words "and practical". – Paul R Nov 29 '12 at 15:20
  • *Programming Pearls is about optimization at the algorithm level*. Yes. But it also write something about *code level optimization* (can you remember the unreadable version of binary search in the book?). – 比尔盖子 Nov 30 '12 at 13:18
  • *premature optimisation being the root of all evil*. I'm fully agree with that. We use tricks when we really have a performance issue. In other cases, just write beautiful code. – 比尔盖子 Nov 30 '12 at 13:24
  • 1
    The PP book makes no claim that the super-extreme optimization of the binary search algorithm is a general technique to be emulated in general. It specifically describes what is possible in extreme cases where performance matters to the utmost. You'd need to demonstrate that the optimized code is less efficient under a modern compiler than the normal looping code, and even then, the book only claims that it was useful when the measurements said it was useful. It advocates profiling to make sure you're expending effort on the relevant parts of your program. If it isn't relevant, don't do it. – Jonathan Leffler Aug 19 '14 at 23:08
1

"Use pointers, not array indexes."

This has never been more efficient. Even the old drafts of ANSI-C specified that they were equivalent:

3.3.2.1 Array subscripting

The definition of the subscript operator [] is that E1[E2] is identical to (*(E1+(E2)))

"Don't use small functions (such as swap()), use macros or write the code directly."

This has been obsolete for quite a while. C99 introduced the inline keyword, but even before that, compilers were free to inline parts of the code. It makes no sense to write such function-like macros today for efficiency reasons.

"So, the small hacks in the old books (Programming Pearls, The C Programming Language) are useless today? They are just make the code more unreadable?"

Please note that what follows here is just my personal opinion and not a consensus among the world's programmer community: I would personally say that those two books are not only useless, they are harmful. Not so much because of various optimization tricks, but mainly because of the horrible, unreadable coding style and the heavy reliance on poorly-defined behavior. Both books are also filled with bugs and typos, so you can't even read them without the errata next to you.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • You might be missing the, um, point about pointers. In *Olden Days* it was often much more efficient to iterate through arrays with pointers rather than array indices, as expressions such as `*p++` typically translated directly into a single machine instruction (PDP-11, 680x0, et al). These days, even on CISC machines with such instructions, good compilers can transform back and forth between array indexing and pointer increments quite easily, but it was not always the case. – Paul R Nov 29 '12 at 14:28
  • @PaulR That sounds like some poor implementation in a specific compiler for a specific architecture, rather than a general problem. I can't remember encountering such issues in old crap compilers, though I admit I have no experience of using compilers made before 1990. And a compiler that did what you described would also get (minor) performance problems because of indirect addressing on the assembler level, instead of direct addressing, which is generally faster on most CPUs. – Lundin Nov 29 '12 at 15:03
  • Well we're talking 1970s/1980s here, which was a very different landscape architecturally - clock speeds in the 1 - 10 MHz range, memory in the 10s of kB range, no caches, no RISC, load/store with postincrement/predecrement in a single instruction. – Paul R Nov 29 '12 at 15:09
  • @PaulR Many modern microcontrollers have similar hardware specs today, but they don't generate different code if you use pointers, as far as I know. But perhaps that's because those compilers were written in somewhat modern times. – Lundin Nov 29 '12 at 15:21
  • Of course the *cross-compilers* for modern micro-controllers etc run in a modern environment - fast CPUs, lots of memory, etc. In the era I'm talking about the *compilers* ran in the same constrained environment, which seems almost miraculous to me looking back today. Think about how good a compiler would be if it had to run on a PIC ! – Paul R Nov 29 '12 at 15:23
  • @PaulR Still, the environment the compiler runs in shouldn't affect the generated code. And well.. think about how good any program would be if it had to run on a PIC. :) – Lundin Nov 29 '12 at 15:42
  • Some of the analyses that are performed by modern compilers, e.g. for loop transformations etc, need large data structures - this just wouldn't have been possible with the limited memory of systems of that era. And of course compilers were a lot less sophisticated anyway, partly because we knew less and partly because of the hardware limitations. Anyway, the past is another country, as they say... – Paul R Nov 29 '12 at 16:56
  • Oh. I think we should stop talking about it. *premature optimisation being the root of all evil*. We have modern hardware and compilers -- just write readable code. If we have to work with old things or we get really bad performance, we have to use small hacks. – 比尔盖子 Nov 30 '12 at 13:29
0

Those hacks are still useful in case you are not allowed to turn on optimization for whatever reason. Sometimes the compiler will also not be able to optimize code as he does not know about intended and uninteded side effects of a certain piece of code.

It really depends on what requirements you have. To my experience there are still things you can express in better ways in order to make the compiler understanding your intention better. It's always a trade off to sacrifice readability in order to gain a better compilation result.

junix
  • 3,161
  • 13
  • 27
0

Basically, yes. But, if you do find a particularly ridiculous example of a missed optimization opportunity, then you should report it to the developers!

Braindead source code will always produce braindead machine code though: to a certain extent the compiler still has to do what you say, rather than what you meant, although many common idioms are recognised and "fixed" (the rule is that it has got to be impossible to tell that it's been altered without using a debugger).

And then there are still tricks, new and old, that are useful, at least on some architectures.

For example, if you have a loop that counts from 0 to 100 and does something to an array, some compilers might reverse the counter and make it go from 100 down to zero (because comparing against zero is cheaper than against another constant), but they can't do that if you loop has a side effect. If you don't care that the side-effect happens in reverse order then you can get better code if you reverse the counter yourself.

Another useful trick that GCC has is __builtin_expect(expr, bool), with which you can tell the compiler that expr is likely to be true or false, so it can optimize branches accordingly. Similarly, __builtin_unreachable() can tell GCC that something can't happen, so it doesn't have to allow for the case where it does.

In general though, the compiler is good enough that you really don't need to care unless your program spends 90% of its runtime in that one tiny function. (For example, memcpy is still typically written in assembler).

ams
  • 24,923
  • 4
  • 54
  • 75