C++ 模板:模板是否内联?性能有缺点吗?

C++ Templates: Are template ínstantiations inlined? Are there drawbacks in performance?

本文关键字:性能 有缺点 是否 模板 C++      更新时间:2023-10-16

当某些C++实体(如结构、类或函数)被声明为模板时,为所述实体提供的定义只是蓝图,必须实例化。

由于模板实体在声明时必须定义(通常是头文件),我有一个概念,我试图说服自己这是错误的,即当模板实例化后,它将由编译器内联。我想问一下是不是这样?

当我读到以下段落时,这个问题的答案引起了我的怀疑:

"模板可能会导致编译时间变慢,甚至可能更大可执行文件,尤其是使用较旧的编译器。"

较慢的编译时间是显而易见的,因为模板必须实例化,但为什么"可能更大的可执行文件"?应以何种方式对此进行解释?我应该将其解释为"许多函数都是内联的"还是"如果有许多模板实例化,可执行文件的大小会增加,也就是说,同一个模板用许多不同的类型实例化,这会导致同一实体的多个副本在那里"?

在后一种情况下,较大的可执行文件大小是否会导致软件运行速度较慢,因为必须将更多的代码加载到内存中,从而导致昂贵的分页?

此外,由于这些问题在某种程度上也与编译器有关,我对Visual C++编译器很感兴趣。关于大多数编译器所做工作的概括答案也能提供很好的见解。

提前谢谢。

由于模板实体在声明时必须定义(通常是头文件)

不是这样。您可以单独声明和定义模板类、方法和函数,就像您可以声明和定义其他类、方法或函数一样。

我有一个概念,我试图说服自己这是错误的,当模板被实例化后,它将被编译器内联。我想问一下是不是这样?

其中一些可能是,也可能是全部,也可能没有。编译器会做它认为最好的事情。

较慢的编译时间是显而易见的,因为模板必须实例化,但为什么"可能更大的可执行文件"?应以何种方式对此进行解释?

它可以有多种解释。就像一瓶阿斯普林含有警告"可能会引发<在此插入副作用>"一样。

我应该将其解释为"许多函数都是内联的"还是"如果有许多模板实例化,可执行文件的大小会增加,也就是说,同一个模板用许多不同的类型实例化,这会导致同一实体的多个副本在那里"?

您不会有同一实体的多个副本——编译器套件必须注意这一点。即使方法是内联的,方法的地址也会:

  1. 始终存在,并且
  2. 当被多个编译单元引用时是相同的地址

你可能会发现,你开始创建比你想要的更多的类型。例如,std::vector<int>是与std::vector<double>完全不同的类型。CCD_ 3是与CCD_ 4不同的函数。程序中的类型和函数定义的数量可能会快速增长。

在后一种情况下,较大的可执行文件大小是否会导致软件运行速度较慢,因为必须将更多的代码加载到内存中,从而导致昂贵的分页?

分页过多,可能不会。很可能是缓存未命中过多。通常,优化较小的代码是实现良好性能的好策略(在某些情况下,例如访问的数据很少,而且都在缓存中)。

它们是否内联始终取决于编译器。所有类似的实例化都共享非内联模板实例化。

因此,许多想要制作Foo<int>的翻译单元都将共享Foo实例化。显然,如果Foo<int>是一个函数,并且编译器在每种情况下都决定内联它,那么代码就会重复。然而,选择内联是因为这样做的优化似乎很可能优于函数调用。

从技术上讲,可能是模板导致执行速度减慢的一个角落。我从事的软件有超紧密的内部环路,我们为此做了很多性能测量。我们使用了许多模板函数和类,与手工编写代码相比,它还没有表现出退化。

我不能确定,但我认为你必须有这样一种情况:-生成的非内联代码-对多个实例化执行此操作-可能是一个手写的函数-手工编写的函数不会因为是一个函数而受到运行时惩罚(即:没有涉及运行时检查的隐式转换)

然后,您可能会遇到这样的情况:单个手写函数适合CPU的指令缓存,但多个模板实例化不适合。