C++ 链接模板类时的编译性能问题
我编写了一组模板实用程序,允许创建类型的编译时列表,并以函数式编程的方式对其进行操作 代码已经运行了,但是我对它的接口(解释如下)不是很满意,所以我试着重构它,虽然新版本还在运行,但编译速度太慢,无法使用 旧的、有效的代码 我将尝试模仿它的外观,而不复制构成真实版本的数百行代码C++ 链接模板类时的编译性能问题,c++,templates,gcc,g++,C++,Templates,Gcc,G++,我编写了一组模板实用程序,允许创建类型的编译时列表,并以函数式编程的方式对其进行操作 代码已经运行了,但是我对它的接口(解释如下)不是很满意,所以我试着重构它,虽然新版本还在运行,但编译速度太慢,无法使用 旧的、有效的代码 我将尝试模仿它的外观,而不复制构成真实版本的数百行代码 #include <stddef.h> namespace typeList { template<class ...TYPES> class List {
#include <stddef.h>
namespace typeList
{
template<class ...TYPES>
class List
{
public:
TypeList() = delete;
static constexpr size_t size = sizeof...(TYPES);
};
template<class LIST, size_t INDEX>
using get = /*Some elaborate implementation*/;
template<class LIST, size_t INDEX, class TYPE>
using insert = /*Some elaborate implementation*/;
template<class LIST, template<class> class MAPPER>
using map = /*Some elaborate implementation*/;
// There goes much more such "functions" but you should get the gist by now.
}
#包括
名称空间类型列表
{
模板
班级名单
{
公众:
TypeList()=删除;
静态constexpr size\u t size=sizeof…(类型);
};
模板
使用get=/*一些精心设计的实现*/;
模板
使用insert=/*一些精心设计的实现*;
模板
使用map=/*一些精心设计的实现*/;
//这样的“功能”还有很多,但你现在应该已经知道要点了。
}
使用此选项大致如下所示:
using initialList = typeList::List<bool, char>;
using tmp0 = typeList::insert<initialList, 1, void>; // List<bool, void, char>
using tmp1 = typeList::map<tmp0, SomeClass>; // List<SomeClass<bool>, SomeClass<char>, SomeClass<char>>
...
using finalResult = typeList::get<tmp26, 0>;
using initialList = typeList::List<bool, char>;
using finalResult = initialList
::insert<1, void> // List<bool, void, char>
::map<SomeClass> // List<SomeClass<bool>, SomeClass<char>, SomeClass<char>>
...
::get<0>;
使用initialList=typeList::List;
使用tmp0=typeList::insert;//列表
使用tmp1=typeList::map;//列表
...
使用finalResult=typeList::get;
基于此版本的程序在20-30秒的合理时间内编译。包含所有生成类型的预编译头文件需要大约100MB的内存
新代码
我对必须创建大量即时的、一次性的类型感到不高兴,因为它会把代码弄得乱七八糟,而且只是烦人的样板文件。我试图重写我的模板,这样他们就可以链接它们
#include <stddef.h>
namespace typeList
{
template<class ...TYPES>
class List
{
public:
TypeList() = delete;
static constexpr size_t size = sizeof...(TYPES);
template<size_t INDEX>
using get = /*Some elaborate implementation*/;
template<size_t INDEX, class TYPE>
using insert = /*Some elaborate implementation*/;
template<template<class> class MAPPER>
using map = /*Some elaborate implementation*/;
// There goes much more such "functions" but you should get the gist by now.
};
}
#包括
名称空间类型列表
{
模板
班级名单
{
公众:
TypeList()=删除;
静态constexpr size\u t size=sizeof…(类型);
模板
使用get=/*一些精心设计的实现*/;
模板
使用insert=/*一些精心设计的实现*;
模板
使用map=/*一些精心设计的实现*/;
//这样的“功能”还有很多,但你现在应该已经知道要点了。
};
}
使用新版本将如下所示:
using initialList = typeList::List<bool, char>;
using tmp0 = typeList::insert<initialList, 1, void>; // List<bool, void, char>
using tmp1 = typeList::map<tmp0, SomeClass>; // List<SomeClass<bool>, SomeClass<char>, SomeClass<char>>
...
using finalResult = typeList::get<tmp26, 0>;
using initialList = typeList::List<bool, char>;
using finalResult = initialList
::insert<1, void> // List<bool, void, char>
::map<SomeClass> // List<SomeClass<bool>, SomeClass<char>, SomeClass<char>>
...
::get<0>;
使用initialList=typeList::List;
使用finalResult=initialList
::插入//列表
::地图//列表
...
::得到;
我的重构尝试成功了一半。它进行编译,并给出与旧版本完全相同的结果。但是,现在大约需要3-10分钟(通常是10分钟),预编译头文件有2GB。(因此,基本上两个值都提高了20倍)
此外,编译器在工作时最多占用8GB的RAM
问题
问题分为两部分:
列表类中的子句急切地实例化中的所有类型,但它们中的大多数都是模板的,所以我不知道编译器将如何尝试。实际上,在完整版本的代码中,我有一个使用的,它不是一个模板,它让编译器陷入了一个不断实例化进一步结果的循环中。添加虚拟模板参数解决了以下问题:使用
s的模板
将根据需要实例化
还有一件事。我正在编写AVR程序,而我有GCC的最新版本,我不能访问C++标准库。除C标准库之外的任何解决方案都是值得理解的,但我还是希望基于纯C++的一些东西。 < P>免责声明:以下所有内容都是我的一般观察,而且我也不超过90%的百分率确信它们是正确的。
我成功地完成了重构,保持了更合理的编译性能。现在,它大约是原始时间和内存使用量的2倍(比上次尝试少10倍)
我没有一个全面的解决方案来解决这个问题,我怀疑是否存在任何问题,但是有一个关于如何控制这种代码的通用指南
问题的原因
据我所知,编译速度慢的唯一原因是GCC非常渴望尽快实例化任何和所有模板,不管它们是否被使用。
这在较短的程序中并不明显,而且似乎只有在代码足够复杂时才会表现出来
这种行为是病态的,在更复杂的情况下,它可能导致无限递归。
例如,下面的代码编译得很好,但在更复杂的程序中,类似的模式会导致模板实例化深度超过最大值错误
template<class T>
struct Bar;
template<class T>
struct Foo
{
using goDeeper = Bar<Foo<T>>;
};
template<class T>
struct Bar
{
using goDeeper = Foo<Bar<T>>;
};
这里编译器可能会实例化list我有点怀疑这个问题是否有解决方案,但我浪费了太多时间,没有尝试在这里提问:-/我在这里没有看到任何会单独导致这种行为的东西。它可能与您省略的“精心设计的实现”有关。@cdhowie可能与此有关,但它们在重构过程中几乎没有变化(当然,除了它们使用其他“函数”并必须转换为新接口的地方)。我真的不知道如何在问题中包含一个,因为简单的可能不是原因,更复杂的可能是长的,并且基于简单的。最可能的解释是,以前有条件实例化的东西现在被无条件实例化了。是否有任何使用目录
template<class ...TYPES>
class List;
template<class ORIGINAL_LIST, unsigned DEPTH>
class RecursionHelper
{
public:
using result = typename RecursionHelper<List<ORIGINAL_LIST>, DEPTH-1>::result;
};
template<class ORIGINAL_LIST>
class RecursionHelper<ORIGINAL_LIST, 0>
{
public:
using result = ORIGINAL_LIST;
};
template<class ...TYPES>
class List
{
public:
template<unsigned DEPTH>
using recursion = List<List<TYPES...>>;
};