Welcome to MSDN Blogs Sign in | Join | Help

Tony Schreiner's WebLog

Developer - IE | Windows | Graphics. Fighting complexity for 10 years and counting.

Syndication

More Micro-optimizations

Here's another micro-optimization that I'm not very fond of. I should note that we don't really obsess about these things too much internally - in general we have established coding practices and only occasionally debate a few debatable things, usually on the fringes. But they do make reasonable blog fodder and I'm trying to cover ones that haven't been debated ad nauseam.

HRESULT DoSomething(HWND hwndParent, BOOL fFoo, BOOL fBar)
{
    HRESULT hr;

    if (hwndParent != NULL)
    {
        if (fFoo)
        {
            hr = DoOperationWithFoo(hwndParent);
        }
        else if (fBar)
        {
            hr = DoOperationWithBar(hwndParent);
        }
    }

    return hr;
}

Catch the bug? This one's so obvious I'm not going to wait a day: if "hwndParent" is NULL then "hr" is left uninitialized. The micro-optimization here is that not initializing "hr" up front potentially saves a few instructions. The argument is that code size is one of the most critical factors for application performance on a heavily multitasking modern desktop (which is true; nearly all of our code is compiled for minimum size rather than maximum performance) and that this helps because it all adds up. The fix for the people who do not prefer initializing up front is to add "else" clauses to set "hr" to the correct error values. You may guess that with that fix you end up with a similar total number of instructions (or even more in this example), but in many cases initializing up front is certainly redundant.

The counter argument, of course, is that the risk of using uninitialized variables far outweighs the minor performance increase. This is the school of thought that I subscribe to.

The counter-counter argument is that modern compilers are likely to pick this up, and even if they don't our more advanced static analysis tools will.

The counter-counter-counter argument is that I've personally fixed around a dozen bugs in other people's code over the years that were the direct result of doing this, and it's also annoying when stepping through the debugger and seeing garbage values. :-)

For the most part this debate can be skirted by doing what I consider a C++ best practice:

HRESULT hr = DoSomething(hwndBlah, TRUE, TRUE);

That is, reducing the scope of variables as much as possible and avoid splitting initialization from assignment. However, in the above contrived example this isn't really possible.

Published Wednesday, January 25, 2006 8:17 PM by tonyschr

Filed under:

Comments

# re: More Micro-optimizations @ Thursday, January 26, 2006 5:42 AM

"Catch the bug? This one's so obvious I'm not going to wait a day: if "hwndParent" is NULL then "hr" is left uninitialized. "

If "hwndParent" is not NULL and both the other params are false, same thing.

leppie

# re: More Micro-optimizations @ Thursday, January 26, 2006 10:29 AM

"However, in the above contrived example this isn't really possible."

Why not? What's wrong with:

if (hwndParent != NULL)
{
if (fFoo)
{
return DoOperationWithFoo(hwndParent);
}
else if (fBar)
{
return DoOperationWithBar(hwndParent);
}
}
return whatever_the_default_return_code_should_be;

The advantage of this approach is that the compiler will tell you if you leave out the last "return" statement. At least I hope it will, it certainly will in C# or Java but I haven't worked in C++ in a long long time...

Stuart Ballard

# re: More Micro-optimizations @ Thursday, January 26, 2006 2:48 PM

Leppie, yep. ;-)

tonyschr

# re: More Micro-optimizations @ Saturday, January 28, 2006 3:13 AM

Alternate approach... document your assumptions:

assert(0 != hwndParent);
assert(fFoo || fBar);

(Arguably the latter should be fFoo != fBar to give an XOR condition, but from the presented code cannot tell if that would be right.)

Richard

New Comments to this post are disabled
Page view tracker