Sign in to follow this  

Is this sort of 'optimization' necessary?

This topic is 4519 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I see in many pieces of code things like.
partial_calculation=sqrt(pow(abs(x-y))*pow(abs(x-y)));
complete_calculation1+=(partial_calculation*whatever)+something_else;
complete_calculation2+=(partial_calculation*whatever)+something_else;
complete_calculation3+=(partial_calculation*whatever)+something_else;
complete_calculation4+=(partial_calculation*whatever)+something_else;
I mean, code that makes some partial calculations and stores them in a variable and then reuses that variable to calculate some other results, when that partial calculation could have just been inlined in the next lines like so:
complete_calculation1+=(sqrt(pow(abs(x-y))*pow(abs(x-y))))*whatever)+something_else;
complete_calculation2+=(sqrt(pow(abs(x-y))*pow(abs(x-y))))*whatever)+something_else;
complete_calculation3+=(sqrt(pow(abs(x-y))*pow(abs(x-y))))*whatever)+something_else;
complete_calculation4+=(sqrt(pow(abs(x-y))*pow(abs(x-y))))*whatever)+something_else;
So i've been wondering, is this done just for clarity, or is there some benifit to the performance, because when i think of it, it kind of seems like "sqrt(pow(abs(x-y))*pow(abs(x-y)))" is going to be cached and there is no need to store it in another variable in the first place.

Share this post


Link to post
Share on other sites
You can assume the compiler's smart enough to do that for you, or you can take the safe path and store the extra varaible yourself. If the compiler doesn't do it, the second method is bound to be slower.

Share this post


Link to post
Share on other sites
Any compiler that can't do that for you is crap, so it shouldn't be neccessary. But for clarity, I would do it anyway.

Share this post


Link to post
Share on other sites
Unless intrinsics are enabled, the compiler may not be able to assume that a call to pow or abs with a constant value will have a constant result. So in the latter case, the sqrt, pow, etc. could end up getting calculated every time. I'd strongly recommend the former, as it makes sure you don't blow off cycles unnecessarily.

Share this post


Link to post
Share on other sites
Yeah, since it's so easy to do (and gives you cleaner code), you might as well. In the best case, you've made an optimization, at the worst case, you've got cleaner code that performs exactly the same.

No real downside, is there? [wink]

It also makes it a lot easier for you if you have to change that formula later on. (Nothing worse than having to search and replace 30 instances of it... Except when you replace 29 and forget one, and get weird bugs because of it)

So much easier (and safer, and potentially faster) to just store the first calculation in a variable you can use afterwards.

Share this post


Link to post
Share on other sites
Common subexpression elimination.

Quote:
Unless intrinsics are enabled, the compiler may not be able to assume that a call to pow or abs with a constant value will have a constant result. So in the latter case, the sqrt, pow, etc. could end up getting calculated every time.


GCC lets you tag a function with the pure attribute, telling the compiler that the function has no side-effects and that the return value depends only on globals and function parameters. You can go one step further and add the const attribute (which isn't the same thing as using the const keyword in a normal C or C++ sense), which tell the compiler that the function doesn't rely on global memory at all (that includes not dereferencing pointers passed as parameters either). Both allow the compiler to perform common subexpression elimination on the function call.

Share this post


Link to post
Share on other sites
Do it for clarity, because if you are in the habit of trying to 'inline' everything, you are bound to screw up sooner or later (e.g. by not accounting for re-negating a negative properly, or by trying to "simplify" a huge expression manually and messing it up). Also because short code* just looks better :)

* short in the sense of number of things that "appear to be done"; don't go using all one-letter variable names on me now.

Share this post


Link to post
Share on other sites
Is there something like the pure attribute for C#? I know it isn't a keyword but I suppose it might be some obscure attribute.

EDIT: but I suppose unless you're using unsafe code, the compiler could do that automatically. Probably does. How lovely [smile]

Share this post


Link to post
Share on other sites
Whether it's more optimized or not you should store the result and reuse it anyway. Don't embrace code redundancy! Let's weigh the benefits of each:

The one with the partial calculation makes the complex calculation only appear once in code to the person looking at it. One simple identifier is used in the following expressions making it easy to see that it's the same value used. Both a programmer and a compiler can much more easily see that you only need to do those calculations and call that function only once, using the return for all of the remaining calculations. You can give that intermediate calculation a name that identifies what it represents in the following expressions, making it more clear as to what you are doing. You have one more line of written code, but you have much less operations. If you wish to change how the calculation is performed, you only have to change it once for the entire group of statements as opposed to once for each line. Finally, it's possible that the code will be more optimized, since in order to make that optimization the compiler would have to know that the functions you are calling produce the same result every time for the same values.

Now, what exactly do you think you benefit from showing the redundant calculation in code? Sure, you have one less line of code, but you have a lot more operations; most of which are redundant. It is less obvious to a reader or a compiler that the calculation has the same result in each expression. The expression is harder to read. It's possibly less optimized, and finally, it's harder to maintain.

Go with storing the partial calculation.

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
GCC lets you tag a function with the pure attribute, telling the compiler that the function has no side-effects and that the return value depends only on globals and function parameters. You can go one step further and add the const attribute (which isn't the same thing as using the const keyword in a normal C or C++ sense), which tell the compiler that the function doesn't rely on global memory at all (that includes not dereferencing pointers passed as parameters either). Both allow the compiler to perform common subexpression elimination on the function call.

I was thinking something exactly like the const attribute would be really useful in VC++ only the other day. Interesting to know that it exists for GCC. You don't know if there's anything equivalent for VC++ (2005) do you?

Share this post


Link to post
Share on other sites
I have a few of simple guidlines I use to make this decision .. and it has NOTHING to do with optimization of the compiler.

If the partial expression is more than 1 or 2 elements in size I think to see if I can find a good name that embodies what the value of the partial expression is .. if so, I use the partial expression version, with the good name ... which gives the added bonus of documenting it right in the code, for me in the future.

If the partial expression is KNOWN to be costly to calculate, then I do the operation once and store it in a temporary variable (I would not repletadly translate a matrix the exact same way 4 times in a row .. I'd do it once). I STILL spend at least 30 seconds trying to think of a decent name for the beast - just makes life easier.

If the partial expression is not particular complex to write, does not have a good name to embody its meaning, and isn't known to be costly to do ... I STILL refactor it out of my code most of the time, if it just makes the subsequent 4 lines of code even slightly easier to read. And also for the simple sake of the DRY principle ... (Don't Repeat Yourself) ... repeating yourself opens up too much maintenence work, and too much room for error.

Share this post


Link to post
Share on other sites
Quote:
Original post by mattnewport
Quote:
Original post by Fruny
GCC lets you tag a function with the pure attribute, telling the compiler that the function has no side-effects and that the return value depends only on globals and function parameters. You can go one step further and add the const attribute (which isn't the same thing as using the const keyword in a normal C or C++ sense), which tell the compiler that the function doesn't rely on global memory at all (that includes not dereferencing pointers passed as parameters either). Both allow the compiler to perform common subexpression elimination on the function call.

I was thinking something exactly like the const attribute would be really useful in VC++ only the other day. Interesting to know that it exists for GCC. You don't know if there's anything equivalent for VC++ (2005) do you?
The only hint I'm aware of for VC++ otpimization is __assume
It works much like assert - you pass it a condition, but the difference is that assert closes the program when the condition is false, while __assume tells the compiler it doesn't have to consider the false case (so you enter the realm of undefined behavior if the condition is false).
I'm not sure how usefull it is, but it seems like it'd be a good idea to have an assert macro that does assert in debug mode and __assume in release mode just so the compiler can take advantage of the stuff you've tested already.

Share this post


Link to post
Share on other sites
Unless this is code being run in a tight loop or some other part of the code that runs very often, that sort of optimization just really isn't necessary. Are you really going to notice a nanosecond or two? Clarity is more important in my opinion. But definately consider the situation to see if it's necessary.

Share this post


Link to post
Share on other sites
Quote:
Original post by helix
Unless this is code being run in a tight loop or some other part of the code that runs very often, that sort of optimization just really isn't necessary. Are you really going to notice a nanosecond or two?


In his example, there's a sqrt, a pow, and an abs. The abs requires a branch, and with intrinsics disabled the sqrt and pow are going to be a table lookup at the very least (with a potential cache miss). Important during loading time? Probably not. But certainly something to keep an eye out for, especially if it's possible to need the code in even a reasonably fast loop. You lose nothing by arranging it intelligently to begin with.

This is an example of times that you should be conscious of what you're doing beforehand. Will it matter to do this sort of thing 1, 2, or 10 times in your code? Probably not. But slowly, as hundreds of micro-anti-optimizations from across your project start to pile up, things will grind to a halt for no apparent reason.

Share this post


Link to post
Share on other sites
The compiler should optomize that type of thing, but the more important issue is readability. However, since in this case its both optomized and readable, then thats the ideal way to go. Just make sure you pick a good name for that variable.

Share this post


Link to post
Share on other sites
sqrt, pow, & abs can be quite slow, so a partial calc is not unreasonable.

This code needs inline functions though, the partial calc should probably be a parameter to an overloaded operator.

Share this post


Link to post
Share on other sites
Quote:
Original post by Polymorphic OOP
Whether it's more optimized or not you should store the result and reuse it anyway. Don't embrace code redundancy! Let's weigh the benefits of each:

The one with the partial calculation makes the complex calculation only appear once in code to the person looking at it. One simple identifier is used in the following expressions making it easy to see that it's the same value used. Both a programmer and a compiler can much more easily see that you only need to do those calculations and call that function only once, using the return for all of the remaining calculations. You can give that intermediate calculation a name that identifies what it represents in the following expressions, making it more clear as to what you are doing. You have one more line of written code, but you have much less operations. If you wish to change how the calculation is performed, you only have to change it once for the entire group of statements as opposed to once for each line. Finally, it's possible that the code will be more optimized, since in order to make that optimization the compiler would have to know that the functions you are calling produce the same result every time for the same values.

Now, what exactly do you think you benefit from showing the redundant calculation in code? Sure, you have one less line of code, but you have a lot more operations; most of which are redundant. It is less obvious to a reader or a compiler that the calculation has the same result in each expression. The expression is harder to read. It's possibly less optimized, and finally, it's harder to maintain.

Go with storing the partial calculation.
That about sums it up.
It's the same thing as how you should use const's instead of the same number in lots of places. A calculation should be treated no differently.

I've honestly worked on a project where someone duplicated things so much that the project actually had 5 source files that were quite long and differed by only a couple of lines. Not to mention that they managed to write things like an byte to hex-string routine that was almost 100 lines long thanks to a seperate case for each digit, in each place. Don't you just love fixing the same bug 5 times?[sick]

If you don't refactor to remove redundancy you'll often miss the big picture.

Share this post


Link to post
Share on other sites

This topic is 4519 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this