|
-
March 28th, 2012, 11:49 AM
#1
Which offers the best performance?
I have a library that I am trying to clean up (it's got way too much preprocessor in it.) I wanted to ask here to be sure that the replacements I'm making will not show any performance problems.
Preprocessor
Code:
#define AMOUNT 5
#define INC
int foo(int i){
#ifdef INC
return i + AMOUNT;
#else
return i - AMOUNT;
#endif
}
Consts
Code:
const int AMOUNT = 5;
const bool INC = true;
int foo(int i){
if (INC){
return i + AMOUNT;
}
return i - AMOUNT; //not in an else to prevent compiler warnings about reaching end of non-void method
}
Templates
Code:
template <bool INC, int AMOUNT>
int foo(int i){
if (INC){
return i + AMOUNT;
}
return i - AMOUNT; //not in an else to prevent compiler warnings about reaching end of non-void method
}
Each of these approaches should end up producing the same instructions correct? All of the decisions end up being made at compile time and the compiler will remove the compiled code that is inaccessible. I will end up using a mix of const and templates depending on the circumstances of the change, but since it's a time critical library, I want to make sure there is no performance degradation.
Last edited by ninja9578; March 28th, 2012 at 11:53 AM.
-
March 28th, 2012, 02:24 PM
#2
Re: Which offers the best performance?
I think I would use a template for the INC, at least. There is probably no benefit to doing so for AMOUNT.
-
March 28th, 2012, 02:28 PM
#3
Re: Which offers the best performance?
AFAIK, you should get the same results in a release build.
In debug though, your code is compiled literally, so you'll have to pay to evaluate the if. Not true with the pre-proc approach.
The cleanest approach might be to use a compile time trait, something akin to how "distance" forwards to 1 of 2 possible calls?
The problem with your template signature is that your template parameters are not the global mainspace variables, so your callers will have to literally write foo<INC, AMOUNT>(some_number). Unless you are using C++11, where template functions are allowed default template function parameters.
Is your question related to IO?
Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
-
March 28th, 2012, 07:11 PM
#4
Re: Which offers the best performance?
 Originally Posted by ninja9578
...Each of these approaches should end up producing the same instructions correct?
If it's true that each approach ends up with the same instructions (and as pointed out by monarch_dodra, it might be true for a release build although it's probably not true for a debug build), then there is absolutely no difference in performance, and you should choose the one that's clearest to you under the circumstances.
Mike
-
March 29th, 2012, 12:35 AM
#5
Re: Which offers the best performance?
There's another option I would prefer:
Code:
#define INC
const int AMOUNT = 5
#ifdef INC
int foo(int i){
return i + AMOUNT;
}
#else
int foo(int i){
return i - AMOUNT;
}
#endif
Here the preprocessor is kept for conditional compilation of system parts but otherwise replaced with C++ constructs.
Also when you do this code reorganization anyway maybe you could take the opportunity and introduce a version control system.
Last edited by nuzzle; March 29th, 2012 at 12:52 AM.
-
March 29th, 2012, 08:11 AM
#6
Re: Which offers the best performance?
Thanks, I only care about the performance of the release version, while debugging it can be a little slower. nuzzle, I know, I'm creating a svn repo for it too.
-
March 29th, 2012, 10:23 AM
#7
Re: Which offers the best performance?
 Originally Posted by ninja9578
I have a library that I am trying to clean up (it's got way too much preprocessor in it.)
If you primary goal is to “clean up” – I would stay away from the template approach.
As was stated, any decent compiler will optimize your first and second fragments to the same code.
But I question that premise (that you use a decent compiler) based on your comment:
Code:
//not in an else to prevent compiler warnings about reaching end of non-void method
I don’t see any warnings in VS 2010 with /W4. What warning should I get?
Also, the major difference between using #define and global const (at least, to me) is that you only change build parameters to create different configurations in the first case, but you have to change the source code in the second.
Vlad - MS MVP [2007 - 2012] - www.FeinSoftware.com
Convenience and productivity tools for Microsoft Visual Studio:
FeinWindows - replacement windows manager for Visual Studio, and more...
-
March 29th, 2012, 11:50 AM
#8
Re: Which offers the best performance?
VladimirF makes a good point about templates, although, I guess it depends on what is meant by "clean up".
#define's affect every file in the compilation and do not obey scope. Global variables are slightly better but affect every translation unit in which they are included, and in addition are not good practice - at the very least, you would be better encapsulating them into a namespace.
Template's on the other hand can provide a good solution (just as long as by 'clean up' you are not meaning that you want to make things clearer for the average maintainer).
Ninja, you may get a warning with your version of the template, since INC is a compile-time constant. A better template solution that does not rely on #define's would be something along the lines of:
Code:
#include <iostream>
namespace myns
{
enum e_modification_values
{
e_increment = true,
e_amount = 5
};
template <bool Increment, int Amount>
struct mod
{
inline static int result(int value)
{
return value + Amount;
}
};
//Template specialisation of the above
template <int Amount>
struct mod<false, Amount>
{
inline static int result(int value)
{
return value - Amount;
}
};
}
int foo(int value)
{
typedef myns::mod<myns::e_increment, myns::e_amount> modifier;
return modifier::result(value);
}
int main()
{
std::cout << foo(20) << std::endl;
}
From a performance point of view, the above doesn't depend on your compilers ability to optimise. From a code contamination point of view, the above works very well, but at the same time, it is hardly clear as to what is going on to someone that may have little knowledge of how templates work.
To some degree it's swings and roundabouts - the compiler should make a good optimisation of any of the alternate solutions and therefore, it could be argued that the solution that I am providing is possibly pointless for something as trivial as this.
Perhaps a question you should be asking is, how many times will foo() be called?
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|