Reorder inheritance to reduce the number of thunks

Description

There are 3 possible approaches:

If you have the following code:

interface IX : public IInterface { virtual void f(); }; class CX : public CInterface, implement IX { IMPLEMENT_IINTERFACE }

then a call to IX*->f() will generally go through a thunk, which adjusts the this pointer,. and then calls on to main definition. All calls to Link() and Release() will need no thunk

The second case:

class CX : implement IX, public CInterface { IMPLEMENT_IINTERFACE }

This will introduce a thunk for the virtual destructor, but no thunk for f(). It will also pack a following 4 byte number into the space after the link count.

The final version is:

class CX : public CInterfaceOf<IX> { }

This will have no thunks for any of the code, and will reduce the object size by a pointer (8 bytes). (It will also pack a trailing 4 byte member.) It will not be compatible with CInterface, and appears to generate more code than option 2.

So recommendations would be:

i) If an object has large number of instances use CInterfaceOf<X> since the reduction in memory for each object is likely to outweigh any cost.

ii) If the members in an interface are called a large number of times then the second form should be used. Each virtual call will save a sub and a jmp.

iii) otherwise it doesn't matter too much, but the second form is probably to be preferred for new code. Unlikely to be worth going back to change existing definitions though.

FYI

Conclusion

None

Activity

Show:

Mark Kelly July 29, 2016 at 8:04 PM
Edited

With current / default flags we do get some inlining, as you reported:

Considering const void* CSerialStreamBase::dopeek(size32_t, size32_t&)/7960 with 107 size
to be inlined into virtual bool CSerialStreamBase::eos()/7982 in jfile.cpp:5782
Estimated badness is -0.000001, frequency 0.20.
Inlined into virtual bool CSerialStreamBase::eos() which now has time 459 and size 100,net change of +87.

If we add -fno-inline or -finline-limit=0 flag we do not get this inlining, or any inlining as I can tell.

Fyi, there are 100's of other inlines with the current / default flags that we may not want to eliminate, as for example:

Considering void CSimpleInterfaceOf<INTERFACE>::Link() const [with INTERFACE = IMapping]/10196 with 7 size
to be inlined into virtual CCachedFileIO* CLazyFileIOCache::addFile(RemoteFilename&, IFOmode)/8237 in jscm.hpp:214

Mark Kelly July 27, 2016 at 8:20 PM

Some other options to possibly check:

-fno-inline
-fno-inline-small-functions
-fno-indirect-inlining
-fno-inline-functions-called-once
-fno-early-inlining
-finline-limit=0

Mark Kelly July 27, 2016 at 8:07 PM

Yes, it appears we have:
-fno-inline-functions -fno-default-inline both set.

Mark Kelly July 27, 2016 at 8:00 PM

Do we also use -fno-default-inline ?
The docs say:
As required by ISO C++, GCC considers member functions defined within the body of a class to be marked inline even if they are not explicitly declared with the inline keyword. You can override this with -fno-default-inline

Gavin Halliday July 22, 2016 at 8:37 AM

Another interesting observation... Stopping CInterface::Release from being expanded inline generally shrinks the code quite a lot. But some files get bigger.

Eventually I worked out it was down to more code being inlined in those particular functions!

It appears that gcc is inlining functions that aren't explicitly marked as inline. Does this mean -fno-inline-functions is not working?

Fixed
Pinned fields
Click on the next to a field label to start pinning.

Details

Components

Assignee

Reporter

Priority

Compatibility

Minor

Fix versions

Created July 20, 2016 at 10:17 AM
Updated October 10, 2016 at 6:14 PM
Resolved August 10, 2016 at 2:13 PM

Flag notifications