Disucussion: When is -O0 appropriate?

35 posts / 0 new
Last post
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've been quietly restraining myself every time I see Cliff's FAQ#4:

Quote:
If using avr-gcc avoid -O0 optimization at all costs
For many purposes, for production code, and especially when using any of the smaller AVRs, I agree. However, in another thread:
BrianS wrote:
I swap to -O0 when testing small functions so that I don't get caught out by the var being optimised 'away'. It makes debugging a lot easier in AVR Studio.

I completely agree and have my makefile set up to allow me to not optimize modules when I need to debug them. I understand Cliff's "avoid -O0 like the plague" FAQ, but when debugging it's usually a logic bug I'm chasing, not a timing bug. Using -O0 makes that debugging vastly easier.

The issue as I see it is that sometimes it makes sense to use -O0. Advising newbies to avoid -O0 simply introduces a different set of questions. "Why did my my delay loop get optimized away?" (Needs intro to .) "Why is it that the exact sequence of statements I gave in my mind-numbingly simple program not being followed?" (Because your do-nothing statements are being optimized away.) And blah, blah, blah.

When optimized, whole functions can be "inlined", variables are kept in registers, and the code can be apparently "rearranged" to achieve the optimizer's goals. This is a good thing. But, when debugging it can be incredibly confusing and frustrating.

It is my belief that the AVR Studio folks choose -O0 as a default as a way to help newbies through the initial debugging process. Is this the right decision? Well, they have to decide one way or the other, and I tend to agree with them.

I'm reluctant to gainsay Cliff, as his thousands of posts point to far more experience answering these questions than my feeble attempts, but I would like to hear other points of view.

So, I open this up for discussion: Should -O0 be banished to the netherworld as Cliff suggests? Is there some reasonable way of describing the peculiar subtleties of optimization to a newbie without the blanket always or never (or at all costs)? How do we balance the needs for debugging logic against the needs for small fast code?

Stu

PS: Cliff, I recommend FAQ #6: If you think the compiler is wrong, think again. It has far more experience than you do.

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I see two or three points here:

1) Yes, I think Cliff is a little bit too rigid. There are times when debugging un-optimized code is good. But...

2) Debugging un-optimized code, that is later going to be built with optimization for a Real Word App (tm), can be dangerous. You are not debugging the RWA. This leads to...

3) So you actually need to learn the art of debugging optimized code. The problem is that for a noob this is not easy. Indeed, the art of debugging in general is hard to learn. The complication of dealing with optimized code adds to this. Still, there is no way around it - if it's gonna hurt then take the pain as early as possible. Not doing it will ultimately lead to problems Real Soon Now (tm). And some things will not work at all (eg. the delay_ms() and delay_us() functions that should have been used in the first place, rather than ones own delay loop).

In my native language there is a saying that goes something like "It's like peeing in your pants. Initially its warm and cosy, but after a while...". Some things are intrinsically hard, and there are no ways around it. You cannot program embedded systems in C without having some understanding of the generated machine code. You need to learn to read machine code, and you need to learn it fairly early on.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:
I think Cliff is a little bit too rigid.

I'll try and avoid making the rather obvious lewd joke ;) (though Mrs. Lawson has never complained)

Be interesting to see what other feedback comes from this but IMHO the positives of avoiding -O0 FAR outweigh the negatives. Perhaps I'll add a "YMMV" or an "IMHO" to that "FAQ"?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

(though Mrs. Lawson has never complained)

??? That's not what she tells >>me<<.

[a bit more on topic] I've seen the counter-arguments (perhaps from the same people): "Stupid compiler generates xyz sequence when any idiot can see that [these constant expressions can be folded, etc.]!" You will probably never win.

I still say that more "debugging" should be in the head and not lean so heavily on the debugger crutches.

Lee

You can put lipstick on a pig, but it is still a pig.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
I still say that more "debugging" should be in the head and not lean so heavily on the debugger crutches.
I'll grant you that I can usually visualize the (bad) code in my head when someone gives me a bug report, and usually have a fix in mind within a minute or two. However, as a human I have blind spots, and sometimes the only way to see why the code is misbehaving is to step through it line by line.

Steve Maguire in his book Writing Solid Code has an entire chapter devoted to stepping through code and why that is good. If I have added functionality that is not time critical, I almost always will step through the new code first to be sure it's behaving correctly. Granted, that is my coding/debugging style. Unlike Mozart and his music, I cannot write perfect code in my head (or even on the terminal) first time.

In the long run, this may boil down to the way that a particular person debugs his/her code. The problem with many readers of this forum is that they are such newbies that "debugging" is a fairly vague concept.

JohanEkdahl wrote:
2) Debugging un-optimized code, that is later going to be built with optimization for a Real Word App (tm), can be dangerous. You are not debugging the RWA.
HMMMMmmmmm, perhaps. Depends on whether (and how much) performance and size are part of your Real World App. I currently have ~130KB of app in my mega2560 - if I add 10K to the size, does that matter? Not to me. I have already isolated most of the time-dependent code so that optimization level rarely affects operation of the code.

JohanEkdahl wrote:
3) So you actually need to learn the art of debugging optimized code. The problem is that for a noob this is not easy. Indeed, the art of debugging in general is hard to learn.
That certainly is a problem. I've been at this for over 30 years and I'm still learning. Just how steep a learning curve should we demand the newbies climb?

For most newbies, their introduction has been Basic, or perhaps C, but almost certainly on a PC. The debuggers there let them step through the code, look at variables as they change step-by-step, and so on. Micro$oft has even advanced the idea of "Checked" and "Release" builds, so the relatively sophisticated PC programmer understands the difference between large slow "debug" code and small fast optimized "release" code.

Granted the environment in an AVR is different. Granted that they should learn to debug optimized code. But is it the best thing to drop them in the shark-infested waters right off the bat?

Perhaps in my copious spare time ( :wink: ) I'll write a debugging tutorial. On the other hand, it'll probably be ignored by the newbies just like all the other tutorials. *sigh*

I dunno. Just more food for thought.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can't help noticing that it's interesting the different perspectives the folks writing code in 128K/256K devices have compared to those trying to squeeze the last 88 bytes into a mega48 or something!

I've lost count of the times there's been posts on here about an app turning out to be larger than the 2K in someone's Tiny and then with -Os it suddenly drops to 1300 bytes or something.

I guess things like delay.h and the perennial 4 machine cycle timing requirements things are a given when it comes to arguing against -O0. How many people have posted here saying their writes to JTD aren't apparently disabling the JTAG and the solution turns out to be because they took Studio's -O0 default?

But I'm trying not to be partisan - really I am.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think part of the game is that the CPUs in our host computers
are so notoriously lacking free registers the compiler could
use for optimization, so ``optimized code'' there is usually not
all that much different from unoptimized one. When I started Unix
on a Data General machine in the early 1990s, equipped with a
Motorola M88100 CPU, it was quite normal that when debugging
optimized code (which is the only code that make sense on a RISC
CPU, per definitionem [*]), the debugger cursor wildly jumped
around. Debugging optmized AVR code is almost benign, compared
to that.

[*] The entire idea behind RISC was to have a dumb CPU that
could execute everything in a single cycle, but have a smart
compiler that would do the job. Obviously, this requires the
compiler to have optmizations turned on.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.
Please read the `General information...' article before.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Can't help noticing that it's interesting the different perspectives the folks writing code in 128K/256K devices have compared to those trying to squeeze the last 88 bytes into a mega48 or something!
Cliff, I think you've nailed it. The AVR usage (and those writing to this forum) really covers the gamut, from ATTiny to ATmega, and the coding and debugging approach must adapt to the processor used. Advice used for one type of processor may be (and commonly is) totally inappropriate for another.

The -O0 avoidance seems to me to be based on two premises: First, on most AVRs final code size is of the utmost importance; being left with "too much function and not enough memory" is a common problem. Using the optimizer can certainly help this problem. (BTW, I get a kick out of the folks who post a tiny do-nothing programs compiled under gcc 3.4.6 and 4.1.2, note an 8 byte difference, and indignantly exclaim "WTF?!?". Joerg, you have the patience of a saint!)

Second, from a timing perspective, there are certain operations (such as setting JTD to disable JTAG) that simply won't work in the cycle constraints with the optimizer turned off.

From those perspectives, I now understand your FAQ, Cliff.

Unfortunately, not using -O0 introduces the constant drone of people who won't read the forum to find out that an optimizer is at work and it will optimize away their "do-nothing" delay loop. *sigh* Well, if it's not one thing it's another, eh?

Thanks to everyone for letting my explore a (not so) philosophical subject. I value your opinions, even if I have played Devil's Advocate occasionally (and will continue to do so).

Thanks, all!

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For AVR GCC, -O0 is all but useless except for debugging purposes, when dealing with such optimized code that it's hard to follow in the debugger (AVR Studio). In that case, -O0 turns off all optimizations making debugging fairly easy.

However, there are some optimizations (ok, at least one) that are turned off that I think should be turned on even in -O0, that should be benign enough even for debugging. I've had it on my mental list to discuss this on the development lists (Hi Joerg), but haven't gotten around to it.

The subject of default values in the GCC plug-in to AVR Studio is tough. The default is set to -O0. How long does it take for a newbie to figure out that their code is totally bloated and they need to change the optimization setting to -Os? Once the setting is changed to -Os, how many people have had real difficulty in debugging after that?

I'm not really sure of the answers to those questions. The default has historically been -O0. This really distorts the size of your code, at the cost of perhaps simplified debugging.

So, based on Cliff's suggestion, when 4.13 SP1 is released (not the beta that is out now), the default is changed to be -Os. This at least gets people off to a good start. They will probably be able to debug using that setting. If anything weird happens, we can always tell them to throttle down to -O1 or -O0.

Let's see how this works for everyone.

Eric Weddington

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Eric,

Well for my part I think that's brilliant news. Like you say, lets see how many "the watch window isn't working" questions are then generated as a result of it ;)

(I'm guessing a lot less than the ones about code size, delay.h, JTD and other 4 cycle ops etc.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

[whispering] Psssstt! Cliff! Why dont you poke him about the libm.a default now that he's in that generous mood... :wink:

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington]

Pages