为什么概念使C ++编译速度变慢?

时间:2011-06-22 15:46:04

标签: c++11 c++-concepts

它试图做什么样的邪恶魔法!?!

我正在听Q&A session with herb sutter,有一个问题是关于概念的。 Herb提到它使编译器变慢(虽然源保持不变)并且该部分明显大于模板部分。

为什么这样做?我在哪里可以找到关于概念的文档?

3 个答案:

答案 0 :(得分:10)

首先,赫伯没有说概念本身使编译速度变慢。他说,对C ++标准库进行概念化使得使用C ++标准库的任何代码都编译得更慢。

原因归结为几件事。

1:约束模板需要编译时间。

当您声明这样的类时:

template<typename T> class Foo {...};

编译器简单地解析Foo并且做得很少。即使使用两阶段查找,编译器也不会在类Foo的编译中做很多事情。当然,它存储它以供日后使用,但初始传递相对较快。

使用概念约束模板时:

template<ConceptName C> class Foo {...};

编译器必须做一些事情。必须事先检查类型C的每次使用是否符合概念ConceptName。这是编译器在实例化时间之前推迟的额外工作。

检查的概念越多,用于验证类型与概念匹配的编译时间就越多。

2:标准C ++库使用了很多概念。

查看迭代器概念的数量:输入,输出,转发,双向,顺序,连续。委员会正在考虑将它们分解为更多。对于不同的迭代器概念,许多算法都有多个版本。

这不包括范围概念(除了输出之外,每种迭代器概念都有一个),std :: string的字符概念以及各种其他类型的东西。所有这些都必须进行编译和检查。


真正需要快速实现的概念是模块。编译器生成包含一系列预先检查符号的模块文件的能力,然后直接加载该文件而无需经过标准编译过程。直接从解析到符号创建。

请记住:对于每个.cpp文件#include,编译器必须读取该文件并进行编译。即使文件每次执行此操作都是相同的事情,它仍然必须尽职尽责地读取文件并进行处理。如果我们谈论的是概念化std::vector,它必须对模板进行所有概念检查。它仍然必须执行编译时执行的所有标准符号查找。等等。

想象一下,如果编译器不必这样做。想象一下,它是否可以直接从磁盘加载一堆符号和定义。根本没有编译;只需为其他代码引入符号和定义即可使用。

这就像预编译头文件更好。预编译头文件仅限于每个.cpp文件一个,而您可以使用任意数量的模块。

可悲的是,模块在C ++ 0x的过程中很早就被淘汰了。如果没有模块,使用概念约束标准库总是比无约束版本编译得慢。

请注意,Herb误解了模块的用途(并不难,因为该功能的大多数初始概念都是他所谈到的内容:跨平台DLL等)。它们的核心基本目的是帮助编译时间,而不是使跨平台DLL工作。模块本身也不是跨平台的。

答案 1 :(得分:0)

您可能会在ConceptsGCC website上找到有用的资源。这就是他们正在构建的编译器(分支GCC),以查看概念(原谅双关语)是否可行。

我认为费用来自于必须对各种语言结构执行彻底,无处不在和递归的有效性检查,并且鉴于您可以指定一组非常丰富的约束,检查那些可能会非常昂贵。

有点像异常规范的噩梦版本!

答案 2 :(得分:0)

我是来自Incredibuild的Uval,因为这个问题已经很久了(从2011年开始),并且在撰写本文时(2020年)最近发布了一些概念,所以我想澄清两点,只是为了避免误导人们或阻止他们使用概念。

曾经被考虑的概念和现在发布的概念是完全不同的存在。 C ++ 20中发布的概念也被称为“精简概念”,因为与概念的初始设计相比,它们包含的功能有所减少。那么,从概念中删除了什么?

主要区别在于,概念的主要设计不仅用于检查模板使用的正确性,而且还用于检查此模板定义的正确性。例如,假设您有一个类型为list-comprehension的模板,该模板需要具有成员函数ids = [item['track']['id'] for items in response for item in items['items']] 。您可以想象这样一个受约束的函数模板:

write_to_file

现在,使用概念的初始设计,功能模板def write_to_file(ids): with open('id.txt', 'a') as file: file.write("\n".join(ids) + '\n') response = [{'items': [{'track': {'id': '6gaeewGjNiQhWOUkWRSLTa'}}, {'track': {'id': '0TK2YIli7K1leLovkQiNik'}}, {'track': {'id': '6v0lAdFF4haL8xjBIUjtOw'}}, {'track': {'id': '443cnFF139Ql85enXOTWEu'}}, {'track': {'id': '7bBIBrSmHvdMlTC7b9p8Vq'}}, {'track': {'id': '50Ud3ecSw6hsVlmAECRuBV'}}, {'track': {'id': '0sjSgTluUJIdqfyRe4EQ4U'}}, {'track': {'id': '2TfcMUAZ4vH4fTA9eF53v2'}}, {'track': {'id': '0YOUE1TlkgSbkNvWkZkEVp'}}, {'track': {'id': '5itHWf2i5yo9PM9bgq4hmP'}}, {'track': {'id': '3AKm9sJmK0v3wUQYSLuze7'}}]}] ids = [item['track']['id'] for items in response for item in items['items']] print(ids) # ['6gaeewGjNiQhWOUkWRSLTa', '0TK2YIli7K1leLovkQiNik', '6v0lAdFF4haL8xjBIUjtOw', '443cnFF139Ql85enXOTWEu', # '7bBIBrSmHvdMlTC7b9p8Vq', '50Ud3ecSw6hsVlmAECRuBV', '0sjSgTluUJIdqfyRe4EQ4U', '2TfcMUAZ4vH4fTA9eF53v2', # '0YOUE1TlkgSbkNvWkZkEVp', '5itHWf2i5yo9PM9bgq4hmP', '3AKm9sJmK0v3wUQYSLuze7'] write_to_file(ids) 的定义将是不正确的,因为我们使用的是Animal成员函数,该成员函数不是必需表达式的一部分。使用精简的C ++ 20概念,此概念定义就可以了。编译器将不会检查make_sound函数模板的正确性,因为在一个轻量级的概念世界中,开发人员需要正确指定类型的要求。 这种差异可以在编译时间上产生很大的差异。在2016年,有两篇论文考虑了概念是否进入C ++ 17的原因: “Why I want Concepts, and why I want them sooner rather than later”“Why I want Concepts, but why they should come later rather than sooner.” 两者都没有考虑过性能,因此可以很好地说明当时不是问题。

此外,当前的概念设计可能具有一些性能优势。根据{{​​3}}的说法,编译速度最慢的是SFINAE,因为它至少需要尝试(通常)实例化大量类型,然后稍后再放弃它们。概念(取决于实现方式)可能不需要实例化任何模板,实际上可能最终会带来性能优势。