C++模板会使程序变慢吗?

46
我从许多人那里听说使用模板会使代码变慢。这是真的吗?我正在构建一个库,如果不创建模板,则会导致代码管理问题。目前我可以想到两个解决方案:
  • 使用 #define

  • 使用模板并在头文件/库中定义所有可能的类型,但不允许最终用户创建模板实例。

例如:typedef Graph<int> GraphI32; 等。

有没有办法限制用户自行创建各种模板实例呢?

对上述问题的帮助将受到高度重视。


如何减少编译时间:https://softwareengineering.stackexchange.com/questions/177546/how-do-you-handle-increasingly-long-compile-times-when-working-with-templates - Ciro Santilli OurBigBook.com
这是一个关于速度的逻辑后续问题:C++模板会使程序变大吗? - Gabriel Staples
5个回答

72
短答案是否定的。更详细的答案请继续阅读。
正如其他人已经指出的,模板不会产生直接的运行时代价,也就是说它们所有的技巧都发生在编译时。然而,在某些情况下,它们间接地可能导致速度变慢。特别是,每个模板实例(通常)会产生与其他实例分开且唯一的代码。在某些情况下,这可能会导致执行变慢,因为产生了足够多的目标代码,以至于它不能很好地适应缓存。
在代码大小方面:大多数编译器可以并将折叠在一起对于相同的实例化的代码,但那只有当实例化确实完全相同时才是正常情况。编译器不会插入代码来进行最简单的转换,以使两个微小不同的实例化彼此匹配。例如,常规函数调用可以并将 T * 转换为 T const *,因此使用 const 或非 const 参数的调用将使用相同的代码(除非您选择按 const 处理该函数,否则您可能已经明确为这两种情况提供了不同的行为)。对于模板,这是不会发生的,T *T const * 的实例化将导致生成两个完全独立的代码片段。编译器(或链接器)可能能够在事后合并这两个代码片段,但不是完全确定的(例如,我肯定曾经使用过不能这样做的编译器)。
但最终,模板对速度的影响往往是正面的,而不是负面的。

10
缓存耗尽与模板无关。如果编译器必须生成大量代码,那么您也必须手动(没有模板)生成相同的代码,同样存在这种情况。因此,这是一个彻头彻尾的错觉。 - Martin York
1
特别是,模板的每个实例化(通常)会产生与其他实例化不同的独立代码。有一些证据支持这一点,请看这里。大多数实现都会创建多个实例,然后在链接时丢弃所有实例,或者您认为每次使用vector <int>(例如)都会在可执行文件中生成重复的代码吗?如果是这样,那么没有人会使用模板。 - anon
5
实际上,每个现代编译器都能够折叠不同模板实例之间的通用代码,并且可以折叠一般情况下看似不相关的代码段中的通用生成代码。这是一种非常基本的优化。编译器和链接器比你更聪明。 - Terry Mahaffey
3
编译器无法进行优化,因为每个实例化的函数都必须具有唯一的地址,以便函数指针比较能够正常工作。vector<int>::push_back 和 vector<unsigned int>::push_back 会始终具有不同的地址,因此它们永远不会共享相同的二进制代码。这也是为什么内联函数的非内联版本总是被生成的原因。聪明的整体程序优化编译器可以跟踪模板函数是否从未取地址,然后允许它们合并,但我不知道是否有任何编译器实际上这样做了。 - Joseph Garvin
1
@Terry:创建一个虚拟程序,使其生成std::vector<int>和std::vector<unsigned int>,并将一个数字推入两个向量中,然后打印它们。即使使用优化,运行“nm”对生成的可执行文件进行符号查找,可以显示两者的符号(与调试模式相比只有1个冗余符号,但我认为这是由于内联而不是您描述的优化)。 (使用GCC 4.2和-O2) - Joseph Garvin
显示剩余11条评论

26

由于模板实例化发生在编译时,使用模板不会产生运行时成本(事实上,有时使用模板可以在编译时执行某些计算以使程序运行更快)。然而,大量使用模板可能会导致编译时间较长。


7
大部分正确,但模板函数的多次实例化会增加代码大小,可能导致指令缓存未命中并减慢程序速度。在某些情况下,非模板函数可以为多种类型(例如int、short和long)提供服务,并且使用较少的可执行代码。 - John Zwinck
3
@John:编译器只会实例化使用过的实际类。因此,如果使用非模板代码,则与使用模板代码相同,开发人员必须手动生成所有类。因此,尽管从技术上讲是正确的,但这与模板无关,它将是一个设计问题,并且无论是否使用模板都会发生。 - Martin York
1
@Martin York: 例如,我想对int数组、float数组和double数组进行排序。我可以调用三个不同的std::sort实例,或者我可以三次调用qsort。后者是否可能生成更小的代码?当然,你可以说程序员们可能在没有使用模板的情况下创建多个非常相似的函数,但实际上他们通常不会这样做,因为类型会令人震惊。 - Steve Jessop
1
你说,“如果使用非模板代码,开发人员将不得不手动生成所有类,情况将与使用模板代码相同。”我的观点是这是一个草人论。通常情况下,如果使用非模板代码,开发人员不会手动生成所有类,因此不会出现相同的情况。实际上,开发人员会找到其他方法编写某些通用代码,并且任何明智的分析都是针对这种方式进行的,以确定是否使用模板是一个好主意。当然,使用模板是个好主意,只是不是因为如果不使用它,你就必须复制和粘贴。 - Steve Jessop
1
对比一下:问:“用Java还是汇编语言开发应用程序会更快?” 答:“不会。如果你像在汇编中那样在Java中编写,你只会得到一个表示内存映射的海量整数数组,使用这个数组的索引进行加法、乘法等运算,而不是在汇编代码中使用指针。这并不容易。当然,你可能会决定在Java中使用变量、对象和垃圾回收机制。这可能会加快开发速度,但这是一个有意为之的权衡和设计决策,与Java本身无关。” 或者回答:“也许吧” ;-) - Steve Jessop
显示剩余3条评论

16

不,它们并不会。每当你发现自己“听说”了某些事情,但无法命名来源时,几乎可以肯定你所听到的是错误的。实际上,模板往往能加速代码。

与其依赖于听到的东西,最好是阅读一本权威的相关书籍 - 我推荐 C++ Templates - The Complete Guide


8

模板会使编译变慢。但大多数情况下,它可以使程序更快。


6

它确实会使目标代码变大,因为C++会为你使用的每种类型生成代码。但我不认为这会减慢执行速度。我没有数据表明它会。

在编码开发、阅读和维护过程中,它肯定会改善你的生活。我不会让编码城市传说阻止你使用这个明显有用的语言特性。


6
摒弃使用它们已经足够了吗?我认为不是。 - duffymo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接