Click to See Complete Forum and Search --> : How do i avoid those nasty vtable?


Mitsukai
June 10th, 2006, 11:38 PM
Vtables, those nasty memory block that makes each class 4 bytes bigger when it uses virtuals. There is a way to avoid them, and your compiler may even optimize it for a very little tiny bit more speed(if it optimizes virtuals diffrent than normal functions at all).

This could perhaps make pc's work faster, for each class instance it doesnt need to construct a vtable anymore, and doesnt need as much ram anymore. There could be alot of classes used with vtables in all of your applications on your pc.

Imagine a game that uses objects with vtables. It has initialized 1 000 objects and there are 3 create and 3 destroyed evry second thats up 4 000 bytes to much allocated space! and 12 bytes allocated evry second for nothing.

This can easily be accomplished by a "container" structure.
Here is a basic example:
#include <cstdio>

template<typename t_Container = Animal>
struct Animal
{
struct ACTIONS
{
static void Talk(const Animal& me)
{
me.Say("...");
}
};

void Say(const char* text) const
{
printf("%s", text);
}

void Talk(void) const
{
t_Container::ACTIONS::Talk(*this);
}
};

struct Cow: Animal<Cow>
{
struct ACTIONS: Animal::ACTIONS
{
static void Talk(const Animal& me)
{
me.Say("mooo");
}
};
};

int main(void)
{
Cow MyPet;
printf("%s", "My pet says: ");
MyPet.Talk();
while(true);
return(0);
}


Downside:
Yes there is a downside. In my code above, there is this rule: Once a cow always a cow. It cannot say ... anymore, like an animal without race would say.

results:
sizeof(Cow) returned 1, sizeof(CowV) (same class but using a virtual function) returned 4, conclusion my idea worked.
This method is VERY usefull as i stated ealier. But has its downside, but most of the time the downside shulndt be much of a big deal.

SuperKoko
June 11th, 2006, 12:47 AM
This method is VERY usefull as i stated ealier. But has its downside, but most of the time the downside shulndt be much of a big deal.

No, the downside *is* a big deal.
Actually, your class it is not runtime-polymorphic at all.
Since the base class contains the derived class as template parameter, it is even much more useless, than normal inheritance without virtual functions.
So, it is not better than function hiding:

#include <iostream>


struct Animal
{
void Talk() {Say("(I'm an animal)");}
void Say(const char* text) {std::cout << text;}
};

struct Cow:public Animal
{
void Talk()
{
Say("mooo");
}
};

int main() {
Cow cow;
Animal* p=&cow;
p->Talk(); // it calls the base class method
// but at least the LSP principle works a bit (you can have an Animal* pointing to a cow).
cow.Talk(); // it calls the derived class method
}

With this (old-style) class hierarchy, you can at last add new classes derived from Animal.

But with your strangely recurring template pattern, you can't add derived classes... All these derived classes : Animal<Cow>, Animal<AnyOther> have *different* base classes.
Which, basically (except very special cases) kills the whole point of inheritance.


However, if you really have to gain 4 bytes, you can write your own vtable system, using only 1 or 2 bytes which would be an index in a static-storage table of vtable pointers.
It adds a level of indirection, and limits the total number of vtable (256 or 65536), but reduces the space loss.

You can also use "#pragma pack(1)" or an equivalent directive.


This method is VERY usefull as i stated ealier

Give me one real life example where it works better than a simple class hierarchy without virtual functions.

Noddon
June 11th, 2006, 07:31 PM
Imagine a game that uses objects with vtables. It has initialized 1 000 objects and there are 3 create and 3 destroyed evry second thats up 4 000 bytes to much allocated space! and 12 bytes allocated evry second for nothing.


Big deal! A whole FOUR THOUSAND bytes! A whole TWELVE bytes every second!

Just think of the programmer time wasted worrying about a whole TWELVE bytes a second! I mean, at that rate I'd have to wait 2.8 YEARS until my computer ran out of memory. Oh! My! God! My! Computer! Might! Run! Out! Of! Memory! In! Nearly! Three! Years!

Sorry, but I seriously think you're finding non-optimisations for non-problems again. Afterall, you use just as much memory having a pointer to the object as your do for the vtable for the object.

And before you say "Well, if everyone optimised like this computers would run SOOOOOO much faster"... I'd point out that my 1GB RAM machine runnin winXP under rather demanding conditions doesn't hit the memory limit. Unless you have a convincing cache-oriented argument, using more memory doesn't mean slowness.

In a simular vein, if you're coding for embedded systems and 4 bytes per object is an issue, then you wouldn't be using C++ or even C for development. Using hand-hacked assembler you could reduce the code size and reduce the data structure size to it's absolute minimum. No other abstraction level allows that. You probably wouldn't be using dynamic memory allocation at all since that generally requires at least EIGHT bytes per object for maintaining the heap data structure! OH! NO! NOT! EIGHT! BYTES! PER! DYNAMICALLY! ALLOCATED! OBJECT! And you thought an extra FOUR bytes for the vtable was bad!

(My apologies for the degree of sarcasm... it's not been the best of days. Admittedly, it's not /that/ sarcastic either.)

Kheun
June 11th, 2006, 08:20 PM
Originally Posted by Mitsukai
Imagine a game that uses objects with vtables. It has initialized 1 000 objects and there are 3 create and 3 destroyed evry second thats up 4 000 bytes to much allocated space! and 12 bytes allocated evry second for nothing.

If you writing code for embedded devices with small memory footprint, it does matters. In such cases, you should be using C instead of C++. However, if you need to achieve polymorphism in C, you still need to write almost the same code to implement vtable which the C++ compiler usually do a better job than us.