• Security incident: ISF was recently accessed by intruders. Please change your password, and change it anywhere else you used it. Read more

Source code comments, fun or profound.

What gives you faster performance than const is declaring them register.
In C, "register" is only a hint to the compiler:

"May be used for local variables or parameter declarations. It is equivalent to auto, except that it provides a hint to the compiler that the object will be heavily used and should be allocated in a way that minimizes access time."
-- C: A Reference Manual, fifth edition, page 83

The only way to be sure is to time the code, alter it, measure it again, try another change, etc.

This leads to another comment I wrote, against a block of assembly-language that ORed two buffers together to make a third. I forget the exact wording, but it was something along the lines of
; OR together graphics[] and text[], and then write to ShiftOut, which sends the data to the printhead.
; This looks like it could be done faster, but the obvious alternative
; (there was an example block of assembly language code here)
; runs faster than the prefetch queue can run, so the 3-clock instructions will take
; 4 clocks, and therefore run slower than the code below, which takes X clocks per 16 bits
; N.B. ShiftOut takes 17 clocks to shift out all the bits and to return to the Ready state, so don't write to it faster than that

That product had a hardware interface that would write commands from the host device directly into the product's RAM. No protocol, no ISR, just a bus mastering for a couple clocks, so the software would poll the values and look for changes. The person who wrote it commented all those values with
// automagically changed by HW when the host writes to us
both when they were declared, and every time they were used. Otherwise a new person reading the code would see something like
Code:
if( NewModeFlag )
{
  SetNewMode();
  NewModeFlag = 0;
}
They'd get confused when search the code and find that NewModeFlag is initialized to 0, and set only once (here), also to zero. Without the comments they'd not have know that NewModeFlag was set automagically.
Code:
// NewModeFlag is automagically changed by HW when the host writes to us
if( NewModeFlag )
makes it clear what happens.

As others have written, be sure to update the comments when you change the code. People can see what the code is doing by reading the code, but the comments have to explain why. I've got a button that reads "Code as if whoever maintains your code is a violent psychopath who knows where you live". And sometimes said "violent psychopath" is you, because products often have longer lives than we anticipate, and you may have to maintain your own code a few years after you wrote it. The better your comments, the faster you can make the change and get back to the Shiny New Project that you're working on.
 
What gives you faster performance than const is declaring them register.

Sorry, it's a long time since I have been writing code.
Not really. Optimizing compilers are better than you at choosing what to put in registers, and modern compilers ignore the keyword entirely*. register is an artifact of the 70s. Any of these efforts to optimize code (declaring local variables const, etc) are now deprecated - the compiler is going to do it better than you.

* gcc does use the register keyword, but to assign a variable to a specific register to allow you to mix c and assembly code together, which is a different use and has a different syntax, since you have to name which register to assign the variable to.
 
Last edited:
Not really. Optimizing compilers are better than you at choosing what to put in registers, and modern compilers ignore the keyword entirely*. register is an artifact of the 70s. Any of these efforts to optimize code (declaring local variables const, etc) are now deprecated - the compiler is going to do it better than you.

Not to mention that while it's kicking your ass the chip is likely reordering your instructions, doing register renaming, hiding memory latency etc.

Most real algorithms that are processing decent amounts of data are memory bandwidth bound anyway. Optimizing for cache usage can make an insane difference and is probably the biggest issue overlooked by most programmers when designing. Pay attention to the cache when doing your data layout if performance matters at all.
 
I can see I'm very old-fashioned. ;)

In my tome, one golden rule was: Never optimize the code till you have thoroughly debugged it.

But, looking at the code people build these days, I suspect some of them don't debug it at all.

Hans
 
MRC_Hans said:
But, looking at the code people build these days, I suspect some of them don't debug it at all.
I think people try to debug their code.

I haven't been in the commercial software biz for awhile, but I have two good friends/associates who are. They say that young programmers don't debug the way we learned to. It's seems like it's a bit of a thrash-around-until-it-works debugging method, rather than a form-hypotheses-and-test-them method. If this is in fact the case, I'd guess it is because computes are so cheap and available now. Just use them up trying different fixes. When you could only get one or two runs a day, it was better to use them wisely.

Also, I'm not sure what sort of models young programmers have in mind for instruction execution, RAM, long-term memory, and so forth. I learned how to program in assembler, so I have low-level, practical models of the machine in my head, even when programming in modern high-level languages. What do young people have?

~~ Paul
 
I mainly do embedded systems, and often I get some intermittent bugs that depend on timing. They may only happen once an hour, so I have to find out exactly what's happening through the "form-hypotheses-and-test-them method".
 
I think people try to debug their code.

I haven't been in the commercial software biz for awhile, but I have two good friends/associates who are. They say that young programmers don't debug the way we learned to. It's seems like it's a bit of a thrash-around-until-it-works debugging method, rather than a form-hypotheses-and-test-them method. If this is in fact the case, I'd guess it is because computes are so cheap and available now. Just use them up trying different fixes. When you could only get one or two runs a day, it was better to use them wisely.

Also, I'm not sure what sort of models young programmers have in mind for instruction execution, RAM, long-term memory, and so forth. I learned how to program in assembler, so I have low-level, practical models of the machine in my head, even when programming in modern high-level languages. What do young people have?

~~ Paul
It's easy to do the "kids these day" routines.

With that said, debugging has changed. We no longer follow waterfall development, for example, because that mode of development is largely hopelessly outdated. So are the old debugging techniques. At one time humans were cheap compared to computer time, so it made sense to sit down, write pseudocode, review it is a room with a large team, walk through it on paper, write it in code, do another code review and walk through, and then, finally, run it.

Today processing cycles are cheap and humans are expensive. More to the point, humans are fallible. Today the stardard of excellence runs more to: you write a small function (and functions are usually quite small with OO techniques). You immediately compile it and run, puttting a breakpoint at the first line. Then, you step through every line, examining each variable. You litter the code with pre and post conditions using ASSERTS. You inject different values into variables using the debugger, and, if you find a bug, the compiler will compile your change while the code is running and inject it into the still running program.

As always, there are tradeoffs. Detractors will point out the ad-hoc nature of this approach. I'll point out that this enables me to form a far better mental model of the software than code reviews ever did. You can read a function for half an hour and never see the problem. Stepping through code reveals problems that black box testing might never have revealed. This sort of programming allows me to write nearly zero defect code. I can write both faster, and produce higher quality this way. My current tool set does not allow this sort of debugging, and my quality has seriously lagged as a result. I find it much harder to produce defect free code, and it takes much longer to find bugs. With write/compile/debug cycles, I found the bugs while still writing the code, when it was freshest in my mind. Now, I find the bugs later, have to search to find the problem, and then have to sit there and puzzle out what is going wrong.

Naturally, all of that can be abused. So can any technique. I love (sarcastically) people that argue for things like pseudo-code, because otherwise "you are hacking". Every time I've seen pseudocode it was the most ungodly hacking that I've ever seen, because you don't have a compiler to keep you honest. Hackers are going to hack. Planners are going to plan. So I cut to the chase and use my code as my pseudocode - stetching out the grand ideas, then fleshing them out, etc. At every moment I can execute the design to see if it works, and I can use the debugger to make sure I've not introduced any errors. That can look like undisciplined churning out of code, I suppose, to those used to waterfall, top-down, spec-design-pseudo-walkthrough-code-walkthrough-test-integrate-test-release programmers.
 
roger said:
Today processing cycles are cheap and humans are expensive. More to the point, humans are fallible. Today the stardard of excellence runs more to: you write a small function (and functions are usually quite small with OO techniques). You immediately compile it and run, puttting a breakpoint at the first line. Then, you step through every line, examining each variable. You litter the code with pre and post conditions using ASSERTS. You inject different values into variables using the debugger, and, if you find a bug, the compiler will compile your change while the code is running and inject it into the still running program.
This is similar to my style, except that I'm old-fashioned and do it manually rather than with a symbolic debugger. Do you think most "kids these days" do it this way?

As always, there are tradeoffs. Detractors will point out the ad-hoc nature of this approach. I'll point out that this enables me to form a far better mental model of the software than code reviews ever did. You can read a function for half an hour and never see the problem. Stepping through code reveals problems that black box testing might never have revealed. This sort of programming allows me to write nearly zero defect code. I can write both faster, and produce higher quality this way.
My only concern is that this is taking a low-level view of the code, line by line, rather than a higher-level view. Of course, you may be excellent at keeping the high-level view in mind while doing this.

Naturally, all of that can be abused. So can any technique. I love (sarcastically) people that argue for things like pseudo-code, because otherwise "you are hacking". Every time I've seen pseudocode it was the most ungodly hacking that I've ever seen, because you don't have a compiler to keep you honest.
No argument in favor of pseudocode here.

~~ Paul
 
This is similar to my style, except that I'm old-fashioned and do it manually rather than with a symbolic debugger. Do you think most "kids these days" do it this way?
Professionals do it this way. It's why compiler suites have built in these capabilities.


My only concern is that this is taking a low-level view of the code, line by line, rather than a higher-level view. Of course, you may be excellent at keeping the high-level view in mind while doing this.
Well, it's not the alpha and omega of producing zero defect code, but is a huge part of it, IMO.
 
My only concern is that this is taking a low-level view of the code, line by line, rather than a higher-level view. Of course, you may be excellent at keeping the high-level view in mind while doing this.

For my project, we do both low-level tests (typically method-level) and high-level functional tests. The low-level tests are intended to make sure that the methods do what we think they will and should cover all the possibilities. They're highly implementation-specific. The functional tests are there to make sure the app does what it should, so they directly address the spec capabilities and are implementation-agnostic.

That all makes it sound like there's a bright, shining line between the low-level and high-level tests. Of course, there's a lot of gray area.
 
Mmm, I must take up trolling. Derailing my own thread twice, not bad at all. :rolleyes:

Hans
 
In C, "register" is only a hint to the compiler:

"May be used for local variables or parameter declarations. It is equivalent to auto, except that it provides a hint to the compiler that the object will be heavily used and should be allocated in a way that minimizes access time."
-- C: A Reference Manual, fifth edition, page 83

Of course. After all, C is supposed to be portable, so at coding time, in principle you cannot know how many internal registers your hardware platform will have. However, I'm assuming (perhaps wrongly) that the compiler will respect the declaration if at all possible.

Hans
 
Historically, C compilers have shown very little respect for my instructions, explicit or otherwise.

Seems we have been using the same compiler, then. :rolleyes:

Seriously, I once tested the register declaration with a small prime finder and found no difference, but it is quite possible that when a program only has 4-5 variables, they are made register by default.

Hans
 
Do people keep assertions in their code permanently, or is it elided when the system is compiled for distribution?
I leave them in and define NDEBUG before sending it to QA. assert() is a macro that then becomes an empty statement. You must "test what you ship and ship what you test", so you've got to disable the asserts before final testing.
 
The Visual Studio compiler takes care of that with the debug/release build options.
Assertion statements compile only when _DEBUG is defined. When _DEBUG is not defined, the compiler treats assertions as null statements. Therefore, assertion statements have zero overhead in your final release program; you can use them liberally in your code without affecting the performance of your Release version and without having to use #ifdefs.
http://msdn.microsoft.com/en-us/library/ww5t02fa(v=vs.71).aspx
 
Do people keep assertions in their code permanently, or is it elided when the system is compiled for distribution?

~~ Paul
As others have pointed out, when you compile for release the macro generates an empty statement. However, if your customer can tell you "a message box popped up stating 'assertion on line 73433 of rogerscrappycode.cpp; index < 0'", that can be really handy for tracking down the problem, vs "it crashed".

While assertions are nice for catching problems in your code, I find they are even more useful just as an additional discipline - you are forced to think about the correct range of every variable, what would happen if the range was wrong, etc. It often forces you to go back to the calling routines and upgrade them for correctness. Yes, you should be doing that anyway, but the discipline of putting them in your code creates a very strong habit.

A guess you could consider this a derail of the OP, but how many times have you seen in comments "// X must be >= 1". The assertion also serves as comments for expected values and behaviors:

Code:
GPS* getGPS()
{
   ASSERT (MIL1553Bus.isRunning());
   GPS* gps = MIL1553Bus.GPS();

   ASSERT (gps != NULL);
   return gps;
}
So, the asserts are documenting my expectations. You shouldn't be calling this routine if the 1553B (an avionics bus) is not operational. And, there is no provision for the GPS not existing. Depending on context, that should all be very reassuring to me that I coded the function correctly (say, this is in a class that already has elaborate error checking for such things), or a real warning sign that I am assuming a lot of things that aren't necessarily true. Certainly that ASSERT (gps != NULL) screams out at me: if a pointer should never be NULL then I should probably be using a reference, not a pointer.

Sure, I should think about these things anyway, but if part of my required activities for any function is to provide pre/post assertions, I am far more likely to be thinking about these design decisions at every step. And, it is a design decision - endlessly checking error conditions in every function is almost always overkill, but assuming everything is correct just guarantees crashes and/or incorrect behavior. Later on, as my code is inevitably read or modified by others, they can see my thought processes rather clearly, with the added benefit that the compiler is injecting this test code into the executable.

I can understand some people poo-pooing the discipline aspect of this. After all, you should be writing robust code this way anyway. All I can say is that in practice I have found writing asserts helps me think through these things, and that I see a lot of code without asserts that gets a lot of these nuances wrong. And, in the course of development, these ASSERTS get tested thousands of times. You make a change you think is fine, and somewhere in the system an ASSERT gets raised. How much easier to track down a bug when you index into an array with -1 because some C built-in function you are using uses -1 to indicate an error. With asserts you get a pop up box immediately. Without asserts you reference into the array and get some random data back, and you may not ever notice the bug other than "sometimes this acts 'weird'".
 
Last edited:
The Visual Studio compiler takes care of that with the debug/release build options.

http://msdn.microsoft.com/en-us/library/ww5t02fa(v=vs.71).aspx

That still makes me nervous. Many years ago, I had an intermittent bug that I couldn't replicate with the test code in place. It turned out that the problem was caused by an off-by-one error that was causing it to write beyond the end of an array. The test code had created a temporary variable that happened to be in the memory location just beyond the end of the array, and overwriting the temporary var didn't cause a problem. If I deleted the test code, then an important var wound up sitting next to the array.

True, the test code was masking the bug, and I don't approve of that. But it's an example of how deleting some seemingly innocuous test code changed the app's output.

Of course, newer compilers shouldn't allow that sort of off-by-one error, but it still makes me nervous about assuming that the release version will work just like the debug version.
 

ISF - Join now!

Every member here is approved by hand. No bots, no spam, just people who care about evidence and honest debate.

Membership is free!

Create your free account

Back
Top Bottom