473,394 Members | 1,879 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,394 software developers and data experts.

Reducing the virtual function table size...

Consider an an object that that can has 7 or 8 functions. If you create an
abstract base class for the "interface" of the object, well, that means 7 or
8 pure virtual functions right? Well, IMHO, that's way too much... I was
wondering if the following technique is "frowned" upon:
<pseudo-code>
_____________
typedef struct ... vztimespec_t;

class condmutex_base {
public:
enum wait_e {
WAITLOCK, TRYLOCK, WAITCOND
};

enum wake_e {
UNLOCK, SIGNAL, BROADCAST
};

protected:
condmutex_base() {}

public:
virtual ~condmutex_base() {}

private:
virtual bool wait(wait_e, vztimespec_t const*) = 0;
virtual bool wake(wake_e) = 0;

public:
inline void unlock() { wake(wake_e::UNLOCK); }
inline void waitlock() { wait(wait_e::WAITLOCK, 0); }
inline void waitcond() { wait(wait_e::WAITCOND, 0); }
inline bool trylock() { return wait(wait_e::TRYLOCK, 0); }
inline bool signal() { return wake(wake_e::SIGNAL); }
inline bool broadcast() { return wake(wake_e::BROADCAST); }

inline bool timedwaitcond(vztimespec_t const *tspec) {
return wait(wait_e::WAITCOND, tspec);
}
};
_____________


I only have 2 virtual functions now, not counting dtor, ect... Well, 2 is
better than 7, or 8?
Any thoughts?

:^)
Apr 17 '07 #1
11 2384
Chris Thomasson wrote:
Consider an an object that that can has 7 or 8 functions. If you create
an abstract base class for the "interface" of the object, well, that
means 7 or 8 pure virtual functions right? Well, IMHO, that's way too
much...
Why, is it causing you problems?
I was wondering if the following technique is "frowned" upon:
All you are doing is adding an extra layer of indirection in dispatch,
what do you hope to gain from this?
>
<pseudo-code>
_____________
typedef struct ... vztimespec_t;

class condmutex_base {
public:
enum wait_e {
WAITLOCK, TRYLOCK, WAITCOND
};

enum wake_e {
UNLOCK, SIGNAL, BROADCAST
};

protected:
condmutex_base() {}

public:
virtual ~condmutex_base() {}

private:
virtual bool wait(wait_e, vztimespec_t const*) = 0;
virtual bool wake(wake_e) = 0;

public:
inline void unlock() { wake(wake_e::UNLOCK); }
drop the inline.
inline void waitlock() { wait(wait_e::WAITLOCK, 0); }
inline void waitcond() { wait(wait_e::WAITCOND, 0); }
inline bool trylock() { return wait(wait_e::TRYLOCK, 0); }
inline bool signal() { return wake(wake_e::SIGNAL); }
inline bool broadcast() { return wake(wake_e::BROADCAST); }

inline bool timedwaitcond(vztimespec_t const *tspec) {
return wait(wait_e::WAITCOND, tspec);
}
};
_____________


I only have 2 virtual functions now, not counting dtor, ect... Well, 2
is better than 7, or 8?
Any thoughts?

:^)


--
Ian Collins.
Apr 17 '07 #2
"Ian Collins" <ia******@hotmail.comwrote in message
news:58**************@mid.individual.net...
Chris Thomasson wrote:
>Consider an an object that that can has 7 or 8 functions. If you create
an abstract base class for the "interface" of the object, well, that
means 7 or 8 pure virtual functions right? Well, IMHO, that's way too
much...

Why, is it causing you problems?
No problems. Its a personal thing I guess.. ;^)

I see no need for 7 to 8 function pointers when all of its functionality can
be amortized into 2 or 3 low-level pure virtual functions... You can add
extra "functionality" without increasing the virtual table size. I am
willing to take the hit in the actual ::wake(...) or ::wait(...) functions
wrt branching on the enum value.

>I was wondering if the following technique is "frowned" upon:
All you are doing is adding an extra layer of indirection in dispatch,
what do you hope to gain from this?
Amortized virtual function table size... IMHO, you don't want to pass around
"interface" objects that have tons of function pointers... Try to condense 7
to 8 functions of the object as a whole, into 2 or 3 low-level pure virtual
functions...
[...]

Apr 17 '07 #3
On Apr 17, 5:16 am, "Chris Thomasson" <cris...@comcast.netwrote:
>
I see no need for 7 to 8 function pointers when all of its functionality can
be amortized into 2 or 3 low-level pure virtual functions... You can add
extra "functionality" without increasing the virtual table size. I am
willing to take the hit in the actual ::wake(...) or ::wait(...) functions
wrt branching on the enum value.

Amortized virtual function table size... IMHO, you don't want to pass around
"interface" objects that have tons of function pointers... Try to condense 7
to 8 functions of the object as a whole, into 2 or 3 low-level pure virtual
functions...
(Enable usual caveats about implementations not necessarily using
vtables and 4-byte pointers ...)
Well there's premature optimisation, and then there's just madness.
Firstly, you do know that the "7 or 8 function pointers" exist once
per class, not per instance, right ?
Secondly, why do you think that passing around "interface objects that
have tons of function pointers" is a problem? You do know that there
is no copying of vtables going on, right ?
So your problem comes down to "I don't like the idea of a single block
of 32 bytes per condmutex-derived class in my program, I really want
to push that down to 8 bytes". I think we can all agree that, unless
you have a vast number of subclasses and are in a very contrained
memory environment, this is a bit silly.

Apr 17 '07 #4
On 17 Apr., 03:40, "Chris Thomasson" <cris...@comcast.netwrote:
Consider an an object that that can has 7 or 8 functions. If you create an
abstract base class for the "interface" of the object, well, that means 7 or
8 pure virtual functions right?
I dont see why. As others have explained, the vtable is a per class
entity: the instance just has a pointer to the vtable.
Well, IMHO, that's way too much... I was
wondering if the following technique is "frowned" upon:
Not at all. Actually, I'd often prefer virtual to be private.
>
<pseudo-code>
_____________
typedef struct ... vztimespec_t;

class condmutex_base {
public:
enum wait_e {
WAITLOCK, TRYLOCK, WAITCOND
};

enum wake_e {
UNLOCK, SIGNAL, BROADCAST
};

protected:
condmutex_base() {}

public:
virtual ~condmutex_base() {}

private:
virtual bool wait(wait_e, vztimespec_t const*) = 0;
virtual bool wake(wake_e) = 0;

public:
inline void unlock() { wake(wake_e::UNLOCK); }
inline void waitlock() { wait(wait_e::WAITLOCK, 0); }
inline void waitcond() { wait(wait_e::WAITCOND, 0); }
inline bool trylock() { return wait(wait_e::TRYLOCK, 0); }
inline bool signal() { return wake(wake_e::SIGNAL); }
inline bool broadcast() { return wake(wake_e::BROADCAST); }

inline bool timedwaitcond(vztimespec_t const *tspec) {
return wait(wait_e::WAITCOND, tspec);
}};

_____________

I only have 2 virtual functions now, not counting dtor, ect... Well, 2 is
better than 7, or 8?
It is better in the sense, that in reality only two virtual functions
are needed. The extra storage cost of the extra virtual functions
should not matter in anything but the most extreme cases.

/Peter

Apr 17 '07 #5
In article <11**********************@y5g2000hsa.googlegroups. com>,
peter koch <pe***************@gmail.comwrote:
>On 17 Apr., 03:40, "Chris Thomasson" <cris...@comcast.netwrote:
>Consider an an object that that can has 7 or 8 functions. If you create an
abstract base class for the "interface" of the object, well, that means 7 or
8 pure virtual functions right?
I dont see why. As others have explained, the vtable is a per class
entity: the instance just has a pointer to the vtable.
>Well, IMHO, that's way too much... I was
wondering if the following technique is "frowned" upon:
Not at all. Actually, I'd often prefer virtual to be private.
>>
<pseudo-code>
_____________
typedef struct ... vztimespec_t;

class condmutex_base {
public:
enum wait_e {
WAITLOCK, TRYLOCK, WAITCOND
};

enum wake_e {
UNLOCK, SIGNAL, BROADCAST
};

protected:
condmutex_base() {}

public:
virtual ~condmutex_base() {}

private:
virtual bool wait(wait_e, vztimespec_t const*) = 0;
virtual bool wake(wake_e) = 0;

public:
inline void unlock() { wake(wake_e::UNLOCK); }
inline void waitlock() { wait(wait_e::WAITLOCK, 0); }
inline void waitcond() { wait(wait_e::WAITCOND, 0); }
inline bool trylock() { return wait(wait_e::TRYLOCK, 0); }
inline bool signal() { return wake(wake_e::SIGNAL); }
inline bool broadcast() { return wake(wake_e::BROADCAST); }

inline bool timedwaitcond(vztimespec_t const *tspec) {
return wait(wait_e::WAITCOND, tspec);
}};

_____________

I only have 2 virtual functions now, not counting dtor, ect... Well, 2 is
better than 7, or 8?
It is better in the sense, that in reality only two virtual functions
are needed. The extra storage cost of the extra virtual functions
should not matter in anything but the most extreme cases.
Yes, you do save a few pointers in the vtable, but now each implementation
of wait() and wake() has to separate out the different cases at runtime,
which will likely be done with a big switch() which is implemented by a
jump table, so you are back to the same or greater cost, plus additional
run-time overhead.

Where this might be a win, though, is if there is a significant amount of
common code in wait() and wake() for all cases. Then in this implementation,
that code would exist only once, but would be replicated in each of the
other cases (or more likely pulled out into a common subroutine or two).

Now, consider if you have a derived class that wants to change the behavior
of the signal() method. In this case, the entire wake() method would
have to be re-implemented. With individual functions, only signal()
would need re-implementation. Obviously you expect derived classes,
otherwise what is the point in having virtual functions at all??

--
Marcus Hall
ma****@tuells.org
Apr 18 '07 #6
"marcus hall" <ma****@tuells.orgwrote in message
news:13*************@corp.supernews.com...
In article <11**********************@y5g2000hsa.googlegroups. com>,
[...]
Now, consider if you have a derived class that wants to change the
behavior
of the signal() method. In this case, the entire wake() method would
have to be re-implemented. With individual functions, only signal()
would need re-implementation. Obviously you expect derived classes,
otherwise what is the point in having virtual functions at all??
Why couldn't a derived class just do something like:

class baseclass : public condmutex_base {
// ...

protected:
virtual bool wake(condmutex_base::wake_e flag) {
// ... switch on flag
return whatever;
}

// ...
};
class derived : public baseclass
// ...

private:
bool wake(condmutex_base::wake_e flag) {

// check for signals; we only want to change signals!
if (flag == condmutex_base::SIGNAL) {
// custom stuff here...
return whatever;
}

// call the base class of derived.
return ((baseclass*)this))->wake(flag);
}

// ...
};
I don't see why a derived class would have to implement the "entire" wake
function... If it wants to change the behavior of signal, it test the enum
flag value against signal, and if it matches, then it executes the custom
signal and returns to the caller... Otherwise, it passes control to the
baseclass by invoking its wake function and passing it the enum flag..
Any thoughts?


Apr 20 '07 #7
Chris Thomasson wrote:
"marcus hall" <ma****@tuells.orgwrote in message
news:13*************@corp.supernews.com...
>>In article <11**********************@y5g2000hsa.googlegroups. com>,

[...]
>>Now, consider if you have a derived class that wants to change the
behavior
of the signal() method. In this case, the entire wake() method would
have to be re-implemented. With individual functions, only signal()
would need re-implementation. Obviously you expect derived classes,
otherwise what is the point in having virtual functions at all??


Why couldn't a derived class just do something like:

class baseclass : public condmutex_base {
// ...

protected:
virtual bool wake(condmutex_base::wake_e flag) {
// ... switch on flag
return whatever;
}

// ...
};
class derived : public baseclass
// ...

private:
bool wake(condmutex_base::wake_e flag) {

// check for signals; we only want to change signals!
if (flag == condmutex_base::SIGNAL) {
// custom stuff here...
return whatever;
}

// call the base class of derived.
return ((baseclass*)this))->wake(flag);
}

// ...
};
I don't see why a derived class would have to implement the "entire" wake
function... If it wants to change the behavior of signal, it test the enum
flag value against signal, and if it matches, then it executes the custom
signal and returns to the caller... Otherwise, it passes control to the
baseclass by invoking its wake function and passing it the enum flag..
Any thoughts?
A lot of effort when just using a set of virtual functions would do the
same thing in a much more obvious (and probably efficient) way.

--
Ian Collins.
Apr 20 '07 #8
"Ian Collins" <ia******@hotmail.comwrote in message
news:58*************@mid.individual.net...
Chris Thomasson wrote:
>[...]
>>>Now, consider if you have a derived class that wants to change the
behavior
[...]
>I don't see why a derived class would have to implement the "entire" wake
function...
[...]
>>
Any thoughts?
A lot of effort when just using a set of virtual functions would do the
same thing in a much more obvious (and probably efficient) way.
Okay. I was trying to figure out how many virtual functions are generally
acceptable for an object that a user "usually" creates large numbers of...
Sounds like using 7-8 functions instead of 2-3 is a fairly acceptable
practice. Well, that does make it easier and less error-prone on my end as a
library implementer. Thank you all for your input.

:^)

Apr 20 '07 #9
Chris Thomasson wrote:
"Ian Collins" <ia******@hotmail.comwrote
>>>
A lot of effort when just using a set of virtual functions would do the
same thing in a much more obvious (and probably efficient) way.


Okay. I was trying to figure out how many virtual functions are
generally acceptable for an object that a user "usually" creates large
numbers of... Sounds like using 7-8 functions instead of 2-3 is a fairly
acceptable practice. Well, that does make it easier and less error-prone
on my end as a library implementer. Thank you all for your input.
I think you are missing one very important fact - each *class* with
virtual functions has a virtual function table (vtable). Each
*instance* of the class has a pointer to *the* vtable for that class.
So the number of virtual functions is irrelevant in the context of an
object that a user "usually" creates large numbers of.

Try this:

#include <iostream>

struct X {
virtual void f() {}
virtual void g() {}
};

int main() {
std::cout << sizeof(X) << std::endl;
return 0;
}

--
Ian Collins.
Apr 20 '07 #10
"Ian Collins" <ia******@hotmail.comwrote in message
news:58**************@mid.individual.net...
Chris Thomasson wrote:
>"Ian Collins" <ia******@hotmail.comwrote
>>>>
A lot of effort when just using a set of virtual functions would do the
same thing in a much more obvious (and probably efficient) way.


Okay. I was trying to figure out how many virtual functions are
generally acceptable for an object that a user "usually" creates large
numbers of... Sounds like using 7-8 functions instead of 2-3 is a fairly
acceptable practice. Well, that does make it easier and less error-prone
on my end as a library implementer. Thank you all for your input.
I think you are missing one very important fact -
I think your right.

[...]

I see.

Apr 21 '07 #11
In article <c4******************************@comcast.com>,
cr*****@comcast.net says...

[ ... ]
Okay. I was trying to figure out how many virtual functions are generally
acceptable for an object that a user "usually" creates large numbers of...
Sounds like using 7-8 functions instead of 2-3 is a fairly acceptable
practice. Well, that does make it easier and less error-prone on my end as a
library implementer. Thank you all for your input.
The number of instances of the class is irrelevant, because (at least as
normally implemented) each object only contains a pointer to the vtable
for the class.

The relevant question is how many other classes derive from this one,
and (in particular) whether most of the virtual functions are likely to
be (nearly) meaningless for many of those derived classes. The vtable
for each derived class is a superset of the vtable for its base, so
forcing an extremely large vtable on an extremely large number of
derived classes can become a problem. Problems stemming from this source
seem to be fairly unusual.

--
Later,
Jerry.

The universe is a figment of its own imagination.
Apr 23 '07 #12

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: vijay | last post by:
I have a doubt with size of classed with virtual functions I have declared A,A1,A2 ,B , C, D some classes with no varaibles but a vitual function each, The size of A is as expected 4 bytes with...
3
by: trialproduct2004 | last post by:
Hi all, Can someone tell me how virtual functions works. Like how memory is getting allocated to virtual function. And how to call base class function through derived class pointer. And why...
4
by: Patrick | last post by:
I have a tablespace that contains the LOB data for 3 tables which exist in other tablespaces. Even after reorging the LOB tablespace with the LONG option, the high water mark is still too high. I...
9
by: ypjofficial | last post by:
Hello All, I am defining a class with one virtual function and storing its first 4 bytes ie. the address of the virtual function table to a file.I am again rereading the file in the same program...
8
by: p175 | last post by:
Express C v9.1 I'm having a hell of a job trying to reduce the highwater mark for various tablespaces in Express C. DB2MART advises to reorg several tables with LONGLOBDATA and to disconnect,...
7
by: Markus Svilans | last post by:
Hello, My question involves virtual functions and inheritance. Suppose we have a class structure, that consists of "data" classes, and "processor" classes. The data classes are derived from...
4
by: Anarki | last post by:
##include <iostream> using namespace std; class A { }; class B:virtual public A { }; class C:virtual public A
5
by: Chameleon | last post by:
The code is: ----------------------------------------------- #include <cstdio> #define S(X) printf(#X ": %d\n", sizeof(X)); class Seekable { public: virtual void seek(int to, char from);
5
by: want.to.be.professer | last post by:
For OO design, I like using virtual member function.But considering efficiency, template is better. Look at this program, class Animal { public: virtual void Walk() = 0; }; class Dog
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.