views:

335

answers:

10

I wonder if and how writing "almighty" classes in c++ actually impacts performance.

If I have for example, a class Point, with only uint x; uint y; as data, and have defined virtually everything that math can do to a point as methods. Some of those methods might be huge. (copy-)constructors do nothing more than initializing the two data members.

class Point
{
   int mx; int my;
   Point(int x, int y):mx(x),my(y){};
   Point(const Point& other):mx(other.x),my(other.y){};
 // .... HUGE number of methods....
};

Now. I load a big image and create a Point for every pixel, stuff em into a vector and use them. (say, all methods get called once) This is only meant as a stupid example!

Would it be any slower than the same class without the methods but with a lot of utility functions? I am not talking about virtual functions in any way!

My Motivation for this: I often find myself writing nice and relatively powerful classes, but when I have to initialize/use a ton of them like in the example above, I get nervous. I think I shouldn't.

what I think I know is:

  1. Methods exist only once in memory. (optimizations aside)
  2. Allocation only takes place for the data members, and they are the only thing copied.

So it shouldn't matter. Am I missing something?

+10  A: 

You are right, methods only exist once in memory, they're just like normal functions with an extra hidden this parameter.

And of course, only data members are taken in account for allocation, well, inheritance may introduce some extra ptrs for vptrs in the object size, but not a big deal

Arkaitz Jimenez
+1  A: 

If you are truly worried, you can tell your compiler to inline the constructors. This optimization step should leave you with clean code and clean execution.

carl
+1  A: 

These 2 bits of code are identical:

Point x;
int l=x.getLength();

int l=GetLength(x);

given that the class Point has a non-virtual method getLength(). The first invocation actually calls int getLength(Point &this), an identical signature as the one we wrote in our second example. (*)

This of course wouldn't apply if the methods you're calling are virtual, since everything would go through an extra level of indirection (something akin to the C-style int l=x->lpvtbl->getLength(x)), not to mention that instead of 2 int's for every pixel you'd actually have 3, the extra one being that pointer to the virtual table.

(*) this isn't exactly true, the "this" pointer is passed through one of the cpu registers instead of through the stack, but the mechanism could have easily worked either way.

Blindy
+1  A: 

First: do not optimize prematurely. Second: clean code is easier to maintain than optimized code.

Methods for classes have the hidden this pointer, but you should not worry about it. Most of the time the compiler tries to pass it via register.

Inheritance and virtual function introduce indirections in the appropriate calls (inheritance = constructor / destructor call, virual function - every function call of this function).

Short:

  • Objects you don't create/destroy often can have virtual methods, inheritance and so on as long as it benefits the design.
  • Objects you create/destroy often should be small (few data members) and should not have many virtual methods (best would be none at all - performance wise).
  • try to inline small methods/constructor. This will reduce the overhead.
  • Go for a clean design and refactor if you don't reach the desired performance.

There is a different discussion about classes having large or small interfaces (for example in one of Scott Meyers (More) Effective C++ Books - he opts for minimal interface). But this has nothing to do with performance.

Tobias Langner
Think you mean do rather than don't in your second point. Also in your second point I see no advantage to having few virtual methods, virtual calls are slower so calling a lot of virtual methods has some (small) performance issues.
Elemental
thank you - of course you are right. In case you have few virt. methods, most method calls are direct (no indirection over the vtbl). If you manage to design your class without any virt. function, no vtbl has to be set up during construction.
Tobias Langner
+5  A: 

You have already got some pretty good technical advice. I want to throw in something non-technical: As the STL showed us all, doing it all in member functions might not be the best way to do this. Rather than piling up arguments, I refer to Scott Meyers' class article on the subject: How Non-Member Functions Improve Encapsulation.

Although technically there should be no problem, you still might want to review your design from a design POV.

sbi
You pointed to one intrigueing article. But my concern is taht these people seem to code much different than most normal people. I love member functions because in every noteworthy IDE they get proposed to me and tell me what that class can do. This is not really possible with non-member non-friend functions.
AndreasT
If a free function doesn't tell you what it does, then it needs a better name.
sbi
Not what I meant. An IDE provides class.<everythingIcandowithclass> which decreases slow lookups in the manuals. Free functions are not displayed there. I can do a search for them, but that is much more hassle than typing '.' and waiting half a sec. It is one of the reasons why people look to "bad style" class only libraries without a lot of template magic and scattered interfaces. Everyone who climbed the mountain of boost mastery can be proud of his achievement, but can only look in envy upon libraries that are usable without a fuss just from the info in a context sensitive tooltip.
AndreasT
Here the IDE will lookup and suggest namespace members when you type `namespace_name::` just the way it does with class members when typing `class_name.` or `class_name->` or `class_name::`.
sbi
A: 

I don't think it makes any difference really

A: 

I have created the same point class as you except it is a template class and all functions are inline. I expect to see performance increase not decrease by this. However, an image of size 800x600 will have 480k pixels and its memory print will be close to 4M without any color information. Not just memory but also initializing 480k object will take too much time. Therefore, I think its not a good idea in that case. However, if you use this class to transform position of an image, or use it for graphic primitives (lines, curves, circles, etc...)

If you drop an email to cemkalyoncu at gmail dot com I will send the code for point object along with some info, eventually I will add it to sourceforge but not yet.

Cem Kalyoncu
A: 

I agree with the above comments wrt:performance and class layout, and would like to add a comment not yet stated about design.

It feels to me like you're over-using your Point class beyond it's real Design scope. Sure, it can be used that way but should it?

In past work in computer games I've often been faced by similar situations, and usually the best end result has been that when doing specialized processing (e.g. image processing) having a specialized code set for that which work on differently laid-out buffers has been more efficient.

This also allows you to performance optimize for the case that matters, in a more clean way, without making base code less maintainable.

In theory, I'm sure that there is a crafty way of using a complex combination of template code, concrete class design, etc., and getting nearly the same run-time efficiency ... but I am usually unwilling to make the complexity-of-implementation trade.

ted_j
+2  A: 

I suppose this is more of an answer than you're looking for, but here goes...

SO is filled with questions where people are worried about the performance of X, Y, or Z, and that worry is a form of guessing.

If you're worried about the performance of something, don't worry, find out.

Here's what to do:

  1. Write the program

  2. Performance tune it

  3. Learn from the experience

What this has taught me, and I've seen it over and over, is this:

  • Best practice says Don't optimize prematurely.

  • Best practice says Do use lots of data structure classes, with multiple layers of abstraction, and the best big-O algorithms, "information hiding", with event-driven and notification-style architecture.

  • Performance tuning reveals where the time is going, which is: Galloping generality, making mountains out of molehills, calling functions & properties with no realization of how long they take, and doing this over multiple layers using exponential time.

  • Then the question is asked: What is the reason behind the best practice for the big-O algorithms, the event- and notification-driven architecture, etc. The answer comes: Well, among other things, performance.

So in a way, best practice is telling us: optimize prematurely. Get the point? It says "don't worry about performance", and it says "worry about performance", and it causes the very thing we're trying unsuccessfully not to worry about. And the more we worry about it, against our better judgement, the worse it gets.

My constructive suggestion is this: Follow steps 1, 2, and 3 above. That will teach you how to use best practice in moderation, and that will give you the best all-around design.

Mike Dunlavey
A: 

Member functions are not copied along with the object. Only data fields contribute to the size of the object.

Matt Fichman