GCC如何在内存中存储成员函数?

时间:2015-05-23 15:20:45

标签: c++ performance gcc optimization compiler-optimization

我正在尝试最小化我的类在内存中占用的大小(包括数据和指令)。我知道如何最小化数据大小,但我不太熟悉GCC如何放置成员函数。

它们是否存储在内存中,与它们在类中声明的顺序相同?

2 个答案:

答案 0 :(得分:3)

出于内存数据表示的目的,C ++ class可以有普通或静态成员函数,或者virtual成员函数(包括一些virtual析构函数,如果有的话)

普通或静态成员函数不占用数据存储器中的任何空间,但是当然它们的编译代码需要一些资源,例如作为文本中的二进制代码或可执行文件或进程的code segment。当然,他们还可以在call stack上需要static数据(或线程局部数据)或本地数据(例如局部变量)。

我的回答是面向Linux的。我不了解Windows,也不知道GCC是如何处理它的。

虚拟成员函数通常通过virtual method table(或 vtable )实现;具有一些虚拟成员函数的class通常具有指向该vtable的单个(假设单个 - inheritance)vtable指针的实例(实际上是文本段中包含的一些数据)。

请注意,vtables不是强制性的,并且不是C ++ 11标准所要求的。但我不知道任何不使用它们的C ++实现。

当你使用multiple-inheritance事情变得更复杂时,对象可能会有几个 vtable指针。

因此,如果您有class(根类或使用单继承),虚拟成员函数的消耗是每个实例一个vtable指针(加上小​​空格) 单个 vtable本身所需要的)。如果你只有一个虚拟成员函数(或析构函数)或者其中的一千个(改变的是vtable本身),它就不会改变(对于每个实例)。每个类都有自己的单个 vtable(除非它没有虚拟成员函数),并且每个实例通常都有一个(对于单继承情况)vtable指针。

GCC编译器可以按照自己的意愿自由组织vtable(它的顺序和布局是你不应该关心的实现细节);另见this。在最近的GCC版本的实践中(对于单继承),vtable指针是对象的第一个单词,vtable包含虚拟方法声明顺序的函数指针,但是你不应该依赖这些细节。

GCC编译器可以按照自己的意愿自由组织代码段中的函数,并且它实际上会对它们进行重新排序(例如,用于优化)。上次我看,它按相反的顺序排序。但你当然不应该依赖那个命令! BTW GCC可以内联函数(即使未标记为inline)并在优化时克隆函数。您还可以使用link-time optimizations编译和链接(例如make CXX='g++ -flto -Os'),然后您可以要求profile-guided optimizationsGCC:{{1} },-fprofile-generate-fprofile-use等...)

您不应该依赖于编译器(和链接器)如何组织功能代码或 vtables 将优化保留给编译器(此类优化取决于您的目标机器,编译器标志和编译器版本)。您也可以使用function attributes向GCC(或Clang / LLVM)编译器提供提示(例如-fauto-profile__attribute__((cold))等等。)

如果你真的需要知道函数是如何放置的(IMHO是非常错误的),请研究生成的汇编代码(例如使用__attribute__((noinline)))并注意它可能因编译器版本而异!

如果您需要在运行时在Linux和Posix系统上从名称中找出函数的地址,请考虑使用dlsym(对于Linux,请参阅dlsym(3),其中也记录g++ -O -fverbose-asm -S })。请注意name mangling,您可以通过声明dladdr等功能来禁用该功能(请参阅C++ dlopen minihowto)。

顺便说一句,您可以编译并链接extern "C"(这对-rdynamic等非常有用......)。如果您确实需要知道函数的地址,请使用nm(1)作为dlopen 您的可执行文件

您可能还会阅读目标平台(和编译器)的ABI规范和calling conventions,例如Linux x86-64 ABI spec

答案 1 :(得分:1)

我们假设我们有一个T类型,有4个实例方法。

class T {
    public:
        void member_function_1() { ... }
        void member_function_2() { ... }
        void member_function_3() { ... }
        void member_function_4() { ... }
};

如果我们实例化T的1个副本,或者我们实例化{100}个T副本,那么这些方法占用的内存量是相同的。