内联函数会导致大小增加多少

How much of an increase in size do inline functions cause?

本文关键字:增加 多少 函数      更新时间:2023-10-16

我最近开始制作GTK+的c++包装器(没什么特别的,只是将所有内容包装到c++类中以方便开发,以便在内部使用),并且为了使已经缓慢的 GTK+的性能最小化,我几乎在所有地方使用内联函数。看几个类函数…

class Widget : public Object
{
public: // A few sample functions. gwidget is the internal GTK+ widget.
    void Show(void) { gtk_widget_show(GTK_WIDGET(gwidget)); }
    void ShowNow(void) { gtk_widget_show_now(GTK_WIDGET(gwidget)); }
    void Hide(void) { gtk_widget_hide(GTK_WIDGET(gwidget)); }
    void ShowAll(void) { gtk_widget_show_all(GTK_WIDGET(gwidget)); }
public: // the internal Gtk+ widget.
    GtkWidget* gwidget;
};

尽管几乎不存在性能膨胀,启动时间和内存使用完全相同,但文件大小急剧增加。a C Gtk+示例窗口生成6.5 kb,而使用我的包装器的示例窗口生成22.5 kb。,所以我需要一点建议。我应该继续使用内联函数吗?我希望我的应用程序是高效的,我可以在文件大小上妥协一点,就像我可以接受它一样,即使一个6.5 kb的C GTK+程序生成像400-500 kb使用我的包装,但不是更多。我不希望我的包装器产生巨大的EXES像wxWidgets或MFC。所以它是值得使用内联函数还是我应该使用正常的?

注意:我所有的函数只占用一行或有时两行,并且不像你在示例中看到的那么大。

我强烈怀疑你是在拿苹果和橘子作比较。

您是否使用完全相同的编译器,相同的编译标志,并使用完全相同的功能制作应用程序?

如果是,反汇编可执行文件,看看额外的代码是什么

我猜这是一些一次性的库代码,用于支持以前未使用的c++特性。

但一如既往,不要猜测,要衡量。

你只有一个数据点。这并不能说明什么。在所有情况下,我们可能会看到文件大小增加350%,或者我们可能会看到固定的16kb开销。你得找出是哪一个。所以得到更多的数据点。扩展应用程序。让它打开10个窗口而不是一个,或者添加额外的功能。在这种情况下,"你的"版本也是三倍大吗?还是大16kb ?还是介于两者之间?获得更多的数据点,您将能够看到文件大小如何缩放

但最有可能的是,你杞人忧天,原因如下:

  • c++编译器将内联视为提示。您正在使编译器更容易内联函数,但决策取决于编译器本身,并且它试图使应用程序更快。如果文件大小开始失去控制,这将减慢你的代码,所以你的编译器将尝试优化更小的文件大小。
  • 您正在查看几个千字节。在一个 tb 硬盘的时代。如果这有可能成为问题,那么您应该能够在测试用例中引发该问题。如果不能编写一个导致文件大小增长超过16kb的测试,那么就不值得担心。
  • 如果文件大小确实成为一个问题,编译器通常会有一个"优化大小"标志。
  • 大型可执行文件的大小通常是因为它们包含大量数据和资源。代码本身很少有问题(除非你对模板元编程完全着迷)

大小差异更可能是由于为c++引入的库,而不是在等效的C中,库开销更少。

如果您的所有包装器代码都遵循上述规则,那么在膨胀方面几乎没有。

在我看来,您的解决方案是实现包装器的一种很有价值的方法。

为了尽量减少已经很慢的Gtk+的性能膨胀,我几乎在所有地方都使用了内联函数

编译器非常擅长知道何时内联和何时不内联函数。inline关键字实际上并不意味着该函数将被内联,只是该函数在不同翻译单元中的定义不会违反单一定义规则(ODR)。

关于代码大小,它将取决于编译器选项和其他一些事情,但对于您呈现的代码应该没有任何影响,就像函数是内联的一样,对一个函数的调用将被替换为对另一个函数的调用,这些都是一行的。请注意,即使所有的使用都是内联的,许多编译器也会创建函数并将其留在二进制文件中,您可能需要查看编译器/链接器文档,了解如何删除它们,但即使创建了它们,项目的大小也不会受到太大影响。

如果你愿意让你的项目从6.5kb增长到400kb,你应该很好。

这取决于

  • 你的功能有多大
  • 你使用它们的频率
  • 编译器设置(您可以影响)
  • 编译器实现(你不能)

等。等。换句话说,不要假设,测量。做一个有或没有内联函数的有代表性的项目块,看看在你的特定情况下效果如何。你从互联网上得到的任何预测充其量都是有根据的猜测。

如果您的函数只有一两行,那么它们不太可能增加生成的二进制文件的大小。实际上,如果代码本身小于函数调用的开销代码,则可以使其更小。

注意,无论哪种方式,开销都可以忽略不计。只需删除对std::sort的单个调用或std::map的实例化就可以抵消任何膨胀。如果您关心代码大小,那么小的内联函数是您最不需要担心的。

您给出的示例应该不会导致调用端的代码增加。替换一个只做一次调用而不做其他事情的内联函数,应该被任何像样的编译器优化。

你必须调查一下真正的增长在哪里。查看所生成的汇编程序,这通常可以很好地了解开销。

这个问题很难回答。这取决于许多因素:

  • 功能复杂度
  • CPU架构和内存模型
  • 执行程序调用约定
  • 参数传递机制,包括对象如何访问this

大小不是唯一要考虑的效率。在许多现代CPU上,执行任何类型的分支或调用指令都可能使CPU停滞,其时间相当于许多指令的时间。通常用函数体中的一些指令替换call指令会带来很大的时间增益。这也可以是一个代码大小的优势,因为CPU寄存器可能已经有函数参数,所以它们不需要被推入或移动。

在出现已知的空间或速度问题之前,不要为小小的优化而紧张。然后寻找影响90%问题的10%的修复。

恭喜你,你正在重新发明GTKmm, GTK+的官方c++绑定。