const OR NOT const
-
And what about compiler's optimisation? What do you think: const helps compiler to produce a faster code or not? With the best regards, Vitaly.
Yes, it does produce faster code. For example: int MyFunction(CString str) { // Use str, but don't change it. // We don't change a member either, we just return something } or: int MyFunction(const CString& str) const { // Use str, but don't change it. // We don't change a member either, we just return something } -- Alex Marbus www.marbus.net
-
Yes, it does produce faster code. For example: int MyFunction(CString str) { // Use str, but don't change it. // We don't change a member either, we just return something } or: int MyFunction(const CString& str) const { // Use str, but don't change it. // We don't change a member either, we just return something } -- Alex Marbus www.marbus.net
Alex, I don't think that constness makes compiler to produce a faster code. Your example shows how using a reference as a parameter instead of a value type makes your code faster. As far as using const is concerned it only states that particular value type, pointer or referenced variable (value or object) cannot be changed; or if const is applied to object method (you cannot use const with static or global functions) it states that such function doesn't change internal state of such object. In other words it makes your design clearer by clarifying your intentions Regards, Andrei Zenkovitch
-
Alex, I don't think that constness makes compiler to produce a faster code. Your example shows how using a reference as a parameter instead of a value type makes your code faster. As far as using const is concerned it only states that particular value type, pointer or referenced variable (value or object) cannot be changed; or if const is applied to object method (you cannot use const with static or global functions) it states that such function doesn't change internal state of such object. In other words it makes your design clearer by clarifying your intentions Regards, Andrei Zenkovitch
Are you sure about that? I'm always certain I was always told that this construction (even without passing a reference) was faster. Anybody else has an opinion about this? Ofcourse I could be wrong, but I like some proof on that :) btw: have a nice weekend all! -- Alex Marbus www.marbus.net
-
Are you sure about that? I'm always certain I was always told that this construction (even without passing a reference) was faster. Anybody else has an opinion about this? Ofcourse I could be wrong, but I like some proof on that :) btw: have a nice weekend all! -- Alex Marbus www.marbus.net
Const does help compilers produce faster code. Imagine this trivial example:
char * const p = something;
char *& ref = something_else;
for (i = 0; i < 1000000; ++i)
{
// do something with p
// do something with ref
}Now - since p is a constant pointer, the compiler knows it cannot change and can avoid reloading p each time it starts the loop. However, if const is removed from p, the compiler cannot assume that p is unchanged (for instance, "ref" can change the value of p), so it will reload p each and every time. This is a very simplified example, but you can extrapolate from there. Please note, that this kind of stuff requires some cooperation from you: If you "cheat" by casting constness away, or changing a "const" value through a reference, the optimizer might product incorrect code! -Oz --- Grab WndTabs from http://www.wndtabs.com to make your VC++ experience that much more comfortable...
-
Const does help compilers produce faster code. Imagine this trivial example:
char * const p = something;
char *& ref = something_else;
for (i = 0; i < 1000000; ++i)
{
// do something with p
// do something with ref
}Now - since p is a constant pointer, the compiler knows it cannot change and can avoid reloading p each time it starts the loop. However, if const is removed from p, the compiler cannot assume that p is unchanged (for instance, "ref" can change the value of p), so it will reload p each and every time. This is a very simplified example, but you can extrapolate from there. Please note, that this kind of stuff requires some cooperation from you: If you "cheat" by casting constness away, or changing a "const" value through a reference, the optimizer might product incorrect code! -Oz --- Grab WndTabs from http://www.wndtabs.com to make your VC++ experience that much more comfortable...
In my tests using VC++ 6, the const keyword may just be superficial. The VC6 compiler is pretty darned smart about optimizing code in release mode -- unless you change the optimization flags. With a small function like you described, the same code will be produce with or without the const keyword. You can verify this yourself by turning on the flag that produces the assembly code listing. I wrote two small functions, the only difference was the use of the const keyword. The reason I also call foo3() is because without it the optimizer threw away all the code in foo1() and foo2(), since these functions did nothing with the results. The assembly code produced was identical for both foo1() and foo2(). int CMyClass::foo3(const char*) { return 0; } int CMyClass::foo1(const CString& str) { char s[255]; for(int i = 0; i < str.GetLength(); i++) s[i] = str[i]; return foo3(s);; } int CMyClass::foo2(CString& str) { char s[255]; for(int i = 0; i < str.GetLength(); i++) s[i] = str[i]; return foo3(s);; } *************************************************************************************** Here is foo1() *************************************************************************************** ; 108 : { sub esp, 256 ; 00000100H ; 109 : char s[255]; ; 110 : for(int i = 0; i < str.GetLength(); i++) xor eax, eax push esi mov esi, ecx mov ecx, DWORD PTR _str$[esp+256] mov edx, DWORD PTR [ecx] mov ecx, DWORD PTR [edx-8] test ecx, ecx jle SHORT $L71804 $L71802: ; 111 : s[i] = str[i]; mov cl, BYTE PTR [edx+eax] mov BYTE PTR _s$[esp+eax+260], cl mov ecx, DWORD PTR [edx-8] inc eax cmp eax, ecx jl SHORT $L71802 $L71804: ; 112 : return foo3(s);; *************************************************************************************** Here is foo2() *************************************************************************************** ; 117 : { sub esp, 256 ; 00000100H ; 118 : char s[255]; ; 119 : for(int i = 0; i < str.GetLength(); i++) xor eax, eax push esi mov esi, ecx mov ecx, DWORD PTR _str$[esp+256] mov edx, DWORD PTR [ecx] mov ecx, DWORD PTR [edx-8] test ecx, ecx jle SHORT $L71813 $L71811: ; 120 : s[i] = str[i]; mov cl, BYTE PTR [edx+eax] mov BYTE PTR _s$[esp+eax+260], cl mov ecx, DWORD PTR [edx-8] inc eax cmp eax, ecx jl SHORT $L71811 $L71813: ; 121 : return foo3(s);;
-
In my tests using VC++ 6, the const keyword may just be superficial. The VC6 compiler is pretty darned smart about optimizing code in release mode -- unless you change the optimization flags. With a small function like you described, the same code will be produce with or without the const keyword. You can verify this yourself by turning on the flag that produces the assembly code listing. I wrote two small functions, the only difference was the use of the const keyword. The reason I also call foo3() is because without it the optimizer threw away all the code in foo1() and foo2(), since these functions did nothing with the results. The assembly code produced was identical for both foo1() and foo2(). int CMyClass::foo3(const char*) { return 0; } int CMyClass::foo1(const CString& str) { char s[255]; for(int i = 0; i < str.GetLength(); i++) s[i] = str[i]; return foo3(s);; } int CMyClass::foo2(CString& str) { char s[255]; for(int i = 0; i < str.GetLength(); i++) s[i] = str[i]; return foo3(s);; } *************************************************************************************** Here is foo1() *************************************************************************************** ; 108 : { sub esp, 256 ; 00000100H ; 109 : char s[255]; ; 110 : for(int i = 0; i < str.GetLength(); i++) xor eax, eax push esi mov esi, ecx mov ecx, DWORD PTR _str$[esp+256] mov edx, DWORD PTR [ecx] mov ecx, DWORD PTR [edx-8] test ecx, ecx jle SHORT $L71804 $L71802: ; 111 : s[i] = str[i]; mov cl, BYTE PTR [edx+eax] mov BYTE PTR _s$[esp+eax+260], cl mov ecx, DWORD PTR [edx-8] inc eax cmp eax, ecx jl SHORT $L71802 $L71804: ; 112 : return foo3(s);; *************************************************************************************** Here is foo2() *************************************************************************************** ; 117 : { sub esp, 256 ; 00000100H ; 118 : char s[255]; ; 119 : for(int i = 0; i < str.GetLength(); i++) xor eax, eax push esi mov esi, ecx mov ecx, DWORD PTR _str$[esp+256] mov edx, DWORD PTR [ecx] mov ecx, DWORD PTR [edx-8] test ecx, ecx jle SHORT $L71813 $L71811: ; 120 : s[i] = str[i]; mov cl, BYTE PTR [edx+eax] mov BYTE PTR _s$[esp+eax+260], cl mov ecx, DWORD PTR [edx-8] inc eax cmp eax, ecx jl SHORT $L71811 $L71813: ; 121 : return foo3(s);;
I think you missed the point. The optimizations aren't really in the routine that contain const parameters, but in the routines that invoke routines with const parameters. Also, in your example, methods were hoisted into the invoking routines also. In general, when the compiler is invoking a method with const parameters it does know that registers that might already contain elements of that const object do not need to be reloaded. Thus, improved performance.
-
In my tests using VC++ 6, the const keyword may just be superficial. The VC6 compiler is pretty darned smart about optimizing code in release mode -- unless you change the optimization flags. With a small function like you described, the same code will be produce with or without the const keyword. You can verify this yourself by turning on the flag that produces the assembly code listing. I wrote two small functions, the only difference was the use of the const keyword. The reason I also call foo3() is because without it the optimizer threw away all the code in foo1() and foo2(), since these functions did nothing with the results. The assembly code produced was identical for both foo1() and foo2(). int CMyClass::foo3(const char*) { return 0; } int CMyClass::foo1(const CString& str) { char s[255]; for(int i = 0; i < str.GetLength(); i++) s[i] = str[i]; return foo3(s);; } int CMyClass::foo2(CString& str) { char s[255]; for(int i = 0; i < str.GetLength(); i++) s[i] = str[i]; return foo3(s);; } *************************************************************************************** Here is foo1() *************************************************************************************** ; 108 : { sub esp, 256 ; 00000100H ; 109 : char s[255]; ; 110 : for(int i = 0; i < str.GetLength(); i++) xor eax, eax push esi mov esi, ecx mov ecx, DWORD PTR _str$[esp+256] mov edx, DWORD PTR [ecx] mov ecx, DWORD PTR [edx-8] test ecx, ecx jle SHORT $L71804 $L71802: ; 111 : s[i] = str[i]; mov cl, BYTE PTR [edx+eax] mov BYTE PTR _s$[esp+eax+260], cl mov ecx, DWORD PTR [edx-8] inc eax cmp eax, ecx jl SHORT $L71802 $L71804: ; 112 : return foo3(s);; *************************************************************************************** Here is foo2() *************************************************************************************** ; 117 : { sub esp, 256 ; 00000100H ; 118 : char s[255]; ; 119 : for(int i = 0; i < str.GetLength(); i++) xor eax, eax push esi mov esi, ecx mov ecx, DWORD PTR _str$[esp+256] mov edx, DWORD PTR [ecx] mov ecx, DWORD PTR [edx-8] test ecx, ecx jle SHORT $L71813 $L71811: ; 120 : s[i] = str[i]; mov cl, BYTE PTR [edx+eax] mov BYTE PTR _s$[esp+eax+260], cl mov ecx, DWORD PTR [edx-8] inc eax cmp eax, ecx jl SHORT $L71811 $L71813: ; 121 : return foo3(s);;
Since your code wasn't testing the same thing as the example I posted, I ran my own little test. As I will show below, it seems the the VC++ optimizer is a bit stupid regarding this particular const optimization. Here's the short program:
void fn_no_const(const char **p, const char ** ref)
{
for (int i = 0; i < 13; ++i)
{
std::cout << **p;
++(*ref);
}
}void fn_const(const char * const * const p, const char ** ref)
{
for (int i = 0; i < 13; ++i)
{
std::cout << **p;
++(*ref);
}
}int main(int argc, char* argv[])
{
const char *p = "Hello World!";
fn_no_const(&p, &p);
p = "Hello World!";
fn_const(&p, &p);
return 0;
}If you look closely, you will see that under optimized conditions, the expected output should be:
Hello World! HHHHHHHHHHHH
This is because in the second function (
fn_const
) p is defined const all around, which should tell the optimizer that the value p points to, and p itself will never change. The optimizer should have then cached the value outside the loop. However, if you look at the disassembly, you will see the the two functions are identicle - no const optimization was performed. This is not to say that VC++ can't or doesn't do const optimization, not that other compilers won't. As a general rule, I try to stickconst
around everything I know to be const. Not only is it a potentially an optimization helper for the compiler, it also (sometimes) helps catch assignments where you think there shouldn't be. -Oz --- Grab WndTabs from http://www.wndtabs.com to make your VC++ experience that much more comfortable... -
I think you missed the point. The optimizations aren't really in the routine that contain const parameters, but in the routines that invoke routines with const parameters. Also, in your example, methods were hoisted into the invoking routines also. In general, when the compiler is invoking a method with const parameters it does know that registers that might already contain elements of that const object do not need to be reloaded. Thus, improved performance.
Maybe that's the way it should work, but the VC6 compiler doesn't optimize it that way. Using the default release-version optimization method, the compiler pushes all arguments onto the stack, and calls the function or method. The invoked function copies the parameters out of stack memory into whatever registers it want to. This compiler does not support the register calling convention, where the calling functions puts arguments into registers and the invoked function uses those register values. In fact, this compiler completly ignores the register keyword altogether.
-
Maybe that's the way it should work, but the VC6 compiler doesn't optimize it that way. Using the default release-version optimization method, the compiler pushes all arguments onto the stack, and calls the function or method. The invoked function copies the parameters out of stack memory into whatever registers it want to. This compiler does not support the register calling convention, where the calling functions puts arguments into registers and the invoked function uses those register values. In fact, this compiler completly ignores the register keyword altogether.
That isn't what I am saying. Lets imagine this case: Routine A has a pointer to a structure called StructGeorge. StructGeorge contains some elements, namely an integer called iBob. During execution, routine A loads the value of iBob into a register. Then routine A invokes routine B which requires the pointer to StructGeorge to be passed to it. After routine B returns, routine A then performs other operations with the value of iBob. Now, if the StructGeorge pointer in routine B is defined as const, then routine A can assume that the value of iBob has not changed and thus does not need to be reloaded (assuming that iBob is still around in a register). If the pointer is not defined as const in routine B, then routine A can not make that assumption and must reload the value.
-
Const does help compilers produce faster code. Imagine this trivial example:
char * const p = something;
char *& ref = something_else;
for (i = 0; i < 1000000; ++i)
{
// do something with p
// do something with ref
}Now - since p is a constant pointer, the compiler knows it cannot change and can avoid reloading p each time it starts the loop. However, if const is removed from p, the compiler cannot assume that p is unchanged (for instance, "ref" can change the value of p), so it will reload p each and every time. This is a very simplified example, but you can extrapolate from there. Please note, that this kind of stuff requires some cooperation from you: If you "cheat" by casting constness away, or changing a "const" value through a reference, the optimizer might product incorrect code! -Oz --- Grab WndTabs from http://www.wndtabs.com to make your VC++ experience that much more comfortable...
IMHO the compiler smartly makes an optimization that if your variable is not a l-value then there is no need to read once it has been read at start. So maybe if an register is free this value goes in there and there are no further reads to that variable throuhout the program. This optimization can be disable by preceding any variable with the keyword 'volatile' which turns off this optimization. So const keyword makes clear to the compiler that this variable cannot be a l-value. Using const with member functions is just a checkpoint that none of the member variables are being changed and if some coder tries changing other coder's code by assigning some values to member vars it will be flagged of as an error keeping the orig coder's intentions intact...