C++ 用模板重写的类会使程序变慢(在运行时)

C++ 用模板重写的类会使程序变慢(在运行时),c++,performance,templates,runtime,C++,Performance,Templates,Runtime,我有一个串行内存2D数组的类,它最初是ints的数组。现在我需要一个具有另一种类型的类似数组,我已经用模板重写了这个类;唯一的区别在于存储对象的类型: template <class T> class Serial2DArray { ... T ** Content; } 下面是实际的类模板: #include <malloc.h> template <class T> class Serial2DArray { public:

我有一个串行内存2D数组的类,它最初是
int
s的数组。现在我需要一个具有另一种类型的类似数组,我已经用模板重写了这个类;唯一的区别在于存储对象的类型:

template <class T>
class Serial2DArray
{
    ...
    T ** Content;
}
下面是实际的类模板:

#include <malloc.h>

template <class T>
class Serial2DArray
{
    public:
    Serial2DArray()
    {
        Content = NULL;
        Width = 0;
        Height = 0;
    }
    Serial2DArray(int _Width, int _Height)
    {
        Initialize(_Width, _Height);
    }
    ~Serial2DArray()
    {
        Deinitialize();
    }
    T ** Content;
    int GetWidth()
    {
        return Width;
    }
    int GetHeight()
    {
        return Height;
    }
    int Initialize(int _Width, int _Height)
    {
        // creating pointers to the beginning of each line
        if((Content = (T **)malloc(_Height * sizeof(T *))) != NULL)
        {
            // allocating a single memory chunk for the whole array
            if((Content[0] = (T *)malloc(_Width * _Height * sizeof(T))) != NULL)
            {
                // setting up line pointers' values
                T * LineAddress = Content[0];
                for(int i=0; i<_Height; ++i)
                {
                    Content[i] = LineAddress; // faster than Content[i] =
                    LineAddress += _Width;    // Content[0] + i * _Width;
                }
                // everything went ok, setting Width and Height values now
                Width = _Width;
                Height = _Height;
                // success
                return 1;
            }
            else
            {
                // insufficient memory available
                // need to delete line pointers
                free(Content);
                return 0;
            }
        }
        else
        {
            // insufficient memory available
            return 0;
        }
    }
    int Resize(int _Width, int _Height)
    {
        // deallocating previous array
        Deinitialize();
        // initializing a new one
        return Initialize(_Width, _Height);
    }
    int Deinitialize()
    {
        // deleting the actual memory chunk of the array
        free(Content[0]);
        // deleting pointers to each line
        free(Content);
        // success
        return 1;
    }
    private:
    int Width;
    int Height;
};
  • 1016832字节
代码如下所示:

Serial2DArray<int> TestArray; 
Serial2DArray<int> ZeroArray;
Serial2DArray TestArray; // NOT-template class with ints
Serial2DArray ZeroArray; // methods are in class declaration
Serial2DArray<int> TestArray;
Serial2DArray<int> ZeroArray;
Serial2DArray<double> AnotherArray;
Serial2DArray<double> YetAnotherArray;
Serial2DArray测试阵列;
Serial2DArray零数组;
串行2个数组和另一个数组;
Serial2DArray-YetAnotherArray;
  • 1017344字节

是的-随机的基准可变性,更不用说整个程序的速度较慢这一事实可能与这个特定的类没有任何关系。

在容器类中使用模板可能会导致模板代码膨胀的已知问题。大致上,这可能会导致程序中出现更多的页面错误,从而降低性能

那你为什么要问?因为模板将为模板的每个类实例而不是一个生成类,从而在二进制产品中生成更多的页面,如果您愿意,可以生成更多的代码页面。根据您的运行时执行情况,这可能会在统计上导致更多的页面错误

查看一个类模板实例和两个最重的实例的二进制文件大小。它将使您掌握新实例引入的新代码大小


以下是关于该主题的维基百科文章:。当强制编译器内联程序中的每个函数和方法时,问题可能是相同的,只要编译器可以使用它。该标准试图通过将
内联
关键字设置为“请求”来防止这种情况,编译器不能每次都遵循该关键字。例如,GCC以中间语言生成代码,以评估生成的二进制文件是否不会导致代码膨胀,并可能因此放弃内联请求。

这是我为测试这个特定类而构建的一个小程序,它只有一个此类对象和两个将元素转换为0的不同函数,还没有修改。这种1-2%的减速在不同的机器和许多后续测试中是一致的。您是否对这两个版本进行了优化编译?是的,并且两个版本的设置都相同。@fynjzn:问题不是这样的。这两个版本可能都是在调试模式下编译的。@ildjarn我理解;两者都处于发布模式。已知的非问题,更像。首先,编译器可以使用相同的汇编程序折叠模板实例。第二,如果你随机跳转,更多的代码只意味着更多的页面错误。但如果最近是这样的话,这种情况已经很久没有出现了。看看布鲁斯·埃克尔的书《用c++思考》。他在书中谈到了这个问题,这可能相当棘手。关于第二点,你是绝对正确的。但这样的话,OP就可以在他的计划中走上这条特殊的道路了。不幸的是,从统计上来说还行。@yves:第一点,至少7年了——不完全是最近的。@yves:讽刺的是,因为你是传播关于代码膨胀的错误信息的人。关于你提供链接的帖子的答案,在我看来都是错误的。注意…的错误感觉。。C++模板给你的性能。以及内联代码中的所有内容。这是一个错误的普遍观点。。。花些时间阅读Bruce Eckel的书“用C++思考”第1或第2卷(不记得在哪一卷中处理了模板代码膨胀的主题),并阅读他处理这个问题的方法。非常有趣。(最初是作为对你答案的评论发布的,你说真正的问题来自将所有代码放在一个标题中)这是什么编译器?似乎这只会给编译器更多的信息(并且不会强迫任何悲观的说法,比如总是内联)。@yvesBaumes好吧,我发布了一个额外的信息作为答案(并且被删除了)——在类声明中包含方法的非模板类也很慢,虽然不是在单独的.cpp中使用方法的模板类(以及类声明中的.h中的头),但速度很快。否则,这两个版本是相同的(只是将实际的方法从类声明移动到类声明)。因此,我开始认为这可能根本不是模板的问题?@fynjzn关于类内声明的慢非模板类,从我的观点来看,原因可能是:编译器将类内声明的方法视为开发人员的内联请求。只要考虑一下访问器,比如通常的getter和setter,就不需要使用inline关键字来请求内联。然后它会加入代码膨胀的问题,但这也意味着你的编译器确实遵循了请求,而它一定没有遵循它(…)等等,如果它是2.9 vs.2.8或29 vs.28,并且是完全可持续的,那么为什么最初的问题会说“我注意到现在它的工作速度慢了1-2%?”?如果你有足够宽的误差条,可以把3.5%的误差称为1-2%,那么这个测量就没有多大用处了。
Serial2DArray<int> TestArray;
Serial2DArray<int> ZeroArray;
Serial2DArray<double> AnotherArray;
Serial2DArray<double> YetAnotherArray;