Advertisement

Unnecessary C++ features?

Started by July 13, 2000 07:53 AM
70 comments, last by null_pointer 24 years, 5 months ago
Porting MFC? If you could do that, I think you''d get an honorary PhD degree from any university you want


Give me one more medicated peaceful moment.
~ (V)^|) |<é!t|-| ~
ERROR: Your beta-version of Life1.0 has expired. Please upgrade to the full version. All important social functions will be disabled from now on.
It's only funny 'till someone gets hurt.And then it's just hilarious.Unless it's you.
Getting back to the performance issue with C++ exceptions. Someone, c++freak I think, said that exceptions cause no performance issue unless they are actually thrown. That, to me, does not seem quite true. Let''s have a look at the code generated by VC++6 (with optimizations turned off, however) for a function that does absoultley nothing with no exceptions vs. exceptions.

    /* void foo(void)   {      // do nothing   }*/// generates this assembly code:183:  void foo(void)184:  {00401020   push        ebp00401021   mov         ebp,esp00401023   sub         esp,40h00401026   push        ebx00401027   push        esi00401028   push        edi00401029   lea         edi,[ebp-40h]0040102C   mov         ecx,10h00401031   mov         eax,0CCCCCCCCh00401036   rep stos    dword ptr [edi]185:      // do nothing186:  }00401038   pop         edi00401039   pop         esi0040103A   pop         ebx0040103B   mov         esp,ebp0040103D   pop         ebp0040103E   ret/* void foo(void)   {      try      {         // do nothing      }      catch(...)      {        }   }*/// generates this assembly code:183:  void foo(void)184:  {00401020   push        ebp00401021   mov         ebp,esp00401023   push        0FFh00401025   push        offset __ehhandler$?foo@@YAXXZ (004106e0)0040102A   mov         eax,fs:[00000000]00401030   push        eax00401031   mov         dword ptr fs:[0],esp00401038   push        ecx00401039   sub         esp,40h0040103C   push        ebx0040103D   push        esi0040103E   push        edi0040103F   mov         dword ptr [ebp-10h],esp00401042   lea         edi,[ebp-50h]00401045   mov         ecx,10h0040104A   mov         eax,0CCCCCCCCh0040104F   rep stos    dword ptr [edi]185:      // do nothing186:      try187:      {00401051   mov         dword ptr [ebp-4],0188:189:      }190:      catch(...)00401058   jmp         __tryend$?foo@@YAXXZ$1 (00401060)191:      {192:193:      }0040105A   mov         eax,offset __tryend$?foo@@YAXXZ$1 (00401060)0040105F   ret194:  }00401060   mov         dword ptr [ebp-4],0FFFFFFFFh00401067   mov         ecx,dword ptr [ebp-0Ch]0040106A   mov         dword ptr fs:[0],ecx00401071   pop         edi00401072   pop         esi00401073   pop         ebx00401074   mov         esp,ebp00401076   pop         ebp00401077   ret    


Perhaps not a great performance hit but indeed a performace hit. I don''t know if optimizations would do any wonders for this though.

"Paranoia is the belief in a hidden order behind the visible." - Anonymous
Advertisement
RWarden:

Isn''t "intuitive" subjective? I understand what you are saying, but you just can''t confine all new types to use the operators as if they were algebraic numbers. Your example shows how operator overloading can easily be misused, but there is an important difference: it was using one operator to do something that had already been defined for another operator (+ instead of *), rather than giving an operator a completely new definition (as in stream I/O).

As with all language tools, operator overloading can be used or abused.

BTW, no one could defend any points because no one''s making an attack here, right? I just explained them a little bit.


Happy Coding!




- null_pointer
Sabre Multimedia
I hate the obscure way C++ is taught... they never tell you why you should do somhting in C++....

Anyways here is a link to STLport (from www.flipcode.com)

http://www.stlport.org/index.shtml

From there, they had a nice link to the SGI stl documentation that is adequate for what I want to know

I'll have to look at that book next month, I am already over my budget. Oh, it is $34.99 at borders.com


"Marriage - when you still get an allowance but call it a 'budget' instead."

Edited by - Whirlwind on July 14, 2000 2:13:04 PM
Here''s some of my thoughts:

--- Performance costs of Exceptions -

Basically, most of the cost is associated with dtor calls during stack unwinding. However, that can happen even when you are not using try, catch, and throw.

For example:

class MyType;

void Func()
{
MyType thing;

thing->Something();
}

This function incurs almost the same performance overhead as the same function including a try/catch block.

A function ( or block scope ) that only uses built-in automatic types ( including pointer to objects ) types and does not use other inline functions, does not incur this penalty.

You shouldn''t worry about the performance penalty of exceptions when one is thrown - after all, that is exceptional

So the bigger issue is how you use your classes.

--- References

Absolutely necessary. I''m not sure if others have pointed out the key difference between a C++ reference and a C++ pointer. A reference CANNOT be null. This is the main reason why passing by const reference is a good thing (tm) - you don''t have to check for null.

The reason why pass by non-const reference is generally considered a bad thing (tm) is because it looks like pass by value in client code, even though it is not.

--- STL

I love it. Who wants to code list and array classes? I want to write code for games!

Although it can be daunting to try to learn it all - just learn what you need to use.

--- volatile

volatile can be essential when you know you''re optimizing compiler may be making assumptions that aren''t necessarily valid about when it needs to reference a real memory location rather than just a register

--- static_cast

I think this is very useful. Changing to the C++ casting operators means using static_cast which will not always let you do the same thing that you can with a C class, and in fact you want this so you can see at compile time if certain types casts are based on bad assumptions

--- object creation at a specific address

Also known as placement new. Basically, this syntax separates raw memory allocation from constructor calling.

This is critical if you want to write your own memory allocation routines for objects.

I have used this to implement block memory allocation template arrays.
I''ve also seen it used for pool allocator of objects.

--- private inheritance

e.g. class A : private B

I''ve used this and I do think it can be useful, although you can often achieve the same thing via composition.
e.g.
class A
{
B iItsB;
};

But private inheritance designates inheriting implementation, as opposed to public inheritance which designates inheriting interface ( and implementation if it comes along with it )



One thing I think I have found no use for in C++ is supporting the old malloc() and free(). Granted, that''s the only way to get at a realloc() and most new and delete implementations are on top of those functions, but eventually those have to go down to real OS specific memory routines.
But being that C++ is ''mostly'' compatibly with C, I can understand why it is in there. I just avoid it.


Getting back to performance issues with C++ exceptions, once more.

On the code presented by Staffan. The function,
void foo(void){} 


in your listing without optimizations should only result in 4 statements. I bet you compiled in Debug mode. Everyone should remember in VC (and all the others with Debug/Release modes from what I know) that the Debug build is not the same as the Release build. The Debug build has alot of extra stuff and ignores most (or is it all?) optimizations. I''m also less sure about the other compilers on this one.

Here''s the 4 lines I get and they sure look like an optimizer ought to know to take the whole damn thing out. Of course, this is a silly function.

push	ebpmov	ebp, esppop	ebpret	0 


Now the try/catch version still had all (or most since I didn''t go do a detailed check) of the code your listing does. For more realistice functions without try/catch I think the savings from code is still somewhat significant though.

And now a reading from the Holy Scriptures, MSDN 4:16.

quote:
The extra overhead associated with the C++ exception handling mechanism may increase the size of executable files and slow program execution time. ...

Because of the nature of exception handling and the extra overhead involved, exceptions should be used only to signal the occurrence of unusual or unanticipated program events. Exception handlers should not be used to redirect the program’s normal flow of control. For example, an exception should not be thrown in cases of potential logic or user input errors, such as the overflow of an array boundary. In these cases, simply returning an error code may be simpler and more concise. Judicious use of exception handling constructs makes your program easier to maintain and your code more readable.


Yeah, don''t throw exceptions everytime your characten hits a wall.

Another major performance hit for exceptions is that unwinding the stack and shifting the ip can cause a series (or would it be just one?) of cache misses. Another reason to only use them for the exceptional "oh my god" errors that aren''t supposed to be happening anyway.

Beware, I will start the "For ''Vs. Threads''" Vs. "Against ''Vs. Threads''" thread if I must.

Mike Roberts
aka milo
mlbobs@telocity.com
Advertisement
Whirlwind: Yeah, I know. I blame a lot of poorly written tutorials and books for not teaching _how_ to use C++. What good is knowing what a feature does, if you don''t know how it fits in with the other features in a language? If you want a great book on C++ that _does_ explain the "how" of the language, check out "C++ for Dummies, 3rd Edition" from IDG Books, Inc. Don''t worry about the title - it''s a solid book with lots of examples and explanations. It''s where I learned most of what I know about C++ (not that I know everything - God forbid!).





- null_pointer
Sabre Multimedia
Anonymous: If you read the post you would have seen I stated the code was taken from code with _all_ optimizations off, yes all of them.

"Paranoia is the belief in a hidden order behind the visible." - Anonymous
quote: Original post by Staffan
Anonymous: If you read the post you would have seen I stated the code was taken from code with _all_ optimizations off, yes all of them.


Yes, I did see that. But Debug Vs. Release is not optimization and that, I believe, is where most of your extra code is coming from with the first function.

And I just noticed that I''ve been showing up as Anonymous Loser, so I''ll have to go figure out WTF is happening with that.

Mike Roberts
aka milo
mlbobs@telocity.com
I don''t know if anyone will read my questions, but this really is a good thread.

There are a few big issues in C++ that I just don''t get (lack of effort I suppose)

1. What is the difference between a typedef and a struct?
2. What do you all use STL for?
3. (Back to game development)Should I use inherited classes and virtual functions for different units, or just one class with a bunch of switches in the code? On the one hand, I have to delete and recreate each time if I use inherited, and some other headaches, but on the other hand the code is more intuitive and readable.
4. Is there a better way to do 2d than DDraw? It is a real pain sometimes.
5. Plain old C sucks (whoah, there, my instincts got the better of me, sorry about that)

Thanks is advance, guys!

Check out my d/l site at here

~BenDilts( void );

This topic is closed to new replies.

Advertisement