【问题标题】:C++ Template Function Instantiation Code SizeC++ 模板函数实例化代码大小
【发布时间】:2013-08-22 01:33:31
【问题描述】:

我正在尝试减少模板函数实例化的数量,但遇到了障碍。

假设我们有以下类(我知道它没有优化:这样做是为了说明问题):

//class no_inherit is implemented the same way as class base (below). 
//This is done to illustrate the issue I'm seeing.
template<typename T, size_t SIZE>
class no_inherit
{
private:
    T m_data[SIZE];
    const size_t m_size;

public:
    no_inherit() :m_size(SIZE){}

    T& operator[](size_t i)
    {return m_data[i];}

    inline size_t size() const
    {return m_size;}
}; 

如下函数:

template<typename T>
void huge_func(T& v)
{
    //..do lots of stuff with v.  For example

    for(size_t i = 0; i < v.size(); ++i)
        v[i] = v[i] + i;

    //...do lots more with v
}

还有如下代码:

int main()
{
    no_inherit<int, 4> v1;
    no_inherit<int, 2> v2;

    huge_func(v1);
    huge_func(v2);
}

huge_func() 会被实例化两次:

void huge_func(no_inherit<int, 4>& v);
void huge_func(no_inherit<int, 2>& v);

由于 huge_func() 非常庞大,我试图通过采用模板参数之一并通过创建以下类层次结构将其转换为动态参数来减少实例化计数:

//Base class only has 1 template parameter.
template<typename T>
class base
{
private:
    T *m_data;  
    const size_t m_size; //hold child's templated size parameter.

protected:
    inline base(T* data, size_t size): m_data(data), m_size(size){}

public:
    T& operator[](size_t i)
    {return m_data[i];}

    inline size_t size() const
    {return m_size;}
};

//Child class has two template parameters
template<typename T, size_t SIZE>
class inherit: public base<T>
{
private:    
    T m_data[SIZE];

public:
    //Pass template parameter to base class
    inherit() : base<T>(m_data, SIZE){}
};

我调用 huge_func() 如下:

int main()
{
    inherit<int, 4> v1;
    inherit<int, 2> v2;

    //make sure only one instantiation of huge_func() is made
    //by using the same type.
    base<int> &v1b = v1;
    base<int> &v2b = v2;

    huge_func(v1b); 
    huge_func(v2b);
}

这只会实例化一个 huge_func() 函数:

void huge_func(base<int>& v);

因此会减少代码大小。

但是唉!当我使用类层次结构时,代码大小会增加。这怎么可能?

如果我有以下代码,那就更奇怪了。

int main()
{
    inherit<int, 4> v1;
    inherit<int, 2> v2;

    huge_func(v1);
    huge_func(v2);
}

代码大小与调用huge_func(v1b)和huge_func(v2b)相同。

编译器在做什么?

【问题讨论】:

  • 您是否真正查看过编译器生成的代码 - 换句话说,您是否确定第一个示例为您提供了两个不同的函数?
  • 是的。在调试版本中,地图文件有两个不同的功能。不幸的是,启用 O3 后,地图文件不包含任何信息,因此那里有点神秘,但我只能假设这两个版本仍然以某种方式、形状或形式存在。
  • 对,所以这可能是编译器在 O3 中内联代码的情况,所以你得到的代码更多,但代码相同。如果您想要更小的代码,请尝试使用 -Os。
  • -OS 存在相同的代码大小问题。事实上,我已经尝试了所有优化(O0、O1、O2、O3 和 OS),结果相同(层次结构生成更大的代码)。

标签: c++ templates optimization size


【解决方案1】:

首先,如果huge_func 确实“巨大”,您可能会受益于将其拆分为几个可重用的较小函数。

除此之外,您还可以对其进行模板化:

template<typename T, int SIZE> void huge_func(no_inherit<T, SIZE>& v)
{
    // function implementation goes here
}

然后你实现它一次,并且你维护你的扁平类结构。

【讨论】:

  • 感谢您的建议。虽然此解决方案将部分解决问题,但在 huge_func() 应接受任何类型(例如,提供 [] 运算符)的情况下效果不佳。换句话说,如果 huge_func() 既要处理数组又要处理类,我不能强制 huge_func() 使用 no_inherit 类。另外,使用您提供的函数签名,将两个不同的 no_inherit 类型传递给函数时,huge_func() 是否仍会实例化两次?
  • 回答你的最后一个问题,首先:是的。每次使用一组新参数调用它时,基本上都是用新类型重载它。如果将函数编译两次是您的担忧,那么您只能通过尝试将其强制转换回已经实例化的东西来解决基派生方法中的问题。您可以通过不将huge_func 设为模板来完成相同的操作(例如,只需将其设为void* 的参数,或允许它接受迭代器等)。这将在您编译的代码中创建该函数的一个实例并保留你的班级结构扁平化。
  • 另外,回答您关于使用基派生方法时代码大小如何增加的问题:使用继承会产生开销。它生成的汇编代码必须做大量维护才能维护vtables。
  • 不会用 base 调用 big_func() 并且只是这样:将数据转换为已经实例化的类型?不幸的是, void* 在这种情况下不会这样做(如果可以的话,那就太好了)。至于迭代器,它们与使用基类/继承类有何不同(我可以看到迭代器工作的唯一方法是通过使 [] 运算符为虚拟来提供额外的间接级别)。
  • 但是我的类没有虚拟例程,所以没有要编译的vtables。
猜你喜欢
  • 1970-01-01
  • 2010-09-18
  • 2011-10-07
  • 1970-01-01
  • 2011-08-12
  • 2012-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多