Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 链接模板类时的编译性能问题_C++_Templates_Gcc_G++ - Fatal编程技术网

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

问题 问题分为两部分:

  • 为什么在这两种情况下编译器的性能会有如此大的差异
  • 有没有可能在不失去与模板交互的良好方式的情况下修复第二个版本的性能
  • 我不知道为什么编译器会有这样的差异。代码的语法有一些变化,但其含义基本相同。看起来编译器必须对20倍以上的数据执行20倍以上的计算

    我最初的猜测是,GCC试图使用
    列表
    类中的
    子句急切地实例化
    中的所有类型,但它们中的大多数都是
    模板
    的,所以我不知道编译器将如何尝试。实际上,在完整版本的代码中,我有一个
    使用的
    ,它不是一个模板,它让编译器陷入了一个不断实例化进一步结果的循环中。添加虚拟模板参数解决了以下问题:使用
    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...>>;
    };