繁体   English   中英

包含在许多转换单元中的静态常量的开销?

[英]Overhead of static constants when included into many translation units?

在头文件中,可以在一行中声明和(预)定义全局常量。

// constants.h
namespace Constant{
    static const unsigned int framerate = 60;
    static const char * const windowName = "Test";
    static const unsigned char * const cursorBitmap = { lots of data };
}

我喜欢这种格式,因为它使我可以将常量保存在一个位置,并且避免在一个文件中声明常量并在另一个文件中定义常量,从而提高了可读性。 但是,当任何转换单位都包含constants.h时,它将按单位在适当位置扩展这些定义。

我的问题是,如果我将constants.h包含在许多转换单元中,例如cursorBitmap和其他数组常量很大,这是否会导致大量开销? 如果我将其包含在100个单位中,我的程序是否将每个字符串和数组文字包含100个副本? 还是只复制指针和值?

如果有开销,是否有一种方法可以避免它而无需分开声明和定义?

(我也猜想在这种用法中“静态”是多余的,但无论如何我都喜欢把它放在那里)

字符串文字是否在各种翻译单元中重复是实现质量的问题。

直接声明的对象将在包含此标头的每个翻译单元中重复。 虽然不多。 在不直接或间接使用某些常量地址的转换单元中,可以对其进行优化。

如果要确保每个常量只有一个副本,甚至没有副本,则可以使用类模板,如下所示:

 #pragma once template< class Dummy > struct Constant_{ static const unsigned int framerate; static const char * const windowName; static const unsigned char * const cursorBitmap; }; template< class Dummy > const unsigned int Constant_<Dummy>::framerate = 60; template< class Dummy > const char * const Constant_<Dummy::windowName = "Test"; template< class Dummy > const unsigned char * const Constant_<Dummy>::cursorBitmap = ...; using Constant = Constant_<void>; 

但是,恕我直言,这比价值还高。

一种类似的替代方法是使用inline函数,每个常数一个。

如果我将其包含在100个单位中,我的程序是否将每个字符串和数组文字包含100个副本? 还是只复制指针和值?

该标准不承诺合并字符串文字,因此取决于实现。 在GNU / Linux上使用GCC 5.1.1进行的简单测试表明,字符串文字不是在未优化的构建中合并的,而是在使用-O-Os时合并的。 但这只是合并实际的char数组。 在某种程度上,编译器没有优化指针或数字常量的存储(如果它们是转换单元的本地存储,不是ODR使用的,并且是POD类型的,则它们显然是在以下情况下消除的候选对象)但是,编译器可能无法轻松合并它们。 该标准要求它们是不同的对象,因此必须具有不同的地址。 即使您要获取其地址,按原样的规则仍可能允许将其删除,但这通常需要实现可能不支持的全局程序优化或链接时优化(包括库),实现可能不支持,仅以有限的方式支持和/或仅取决于编译器和链接器设置。 换句话说,它只会在适当的情况下发生,但更有可能不会发生。

我自己的测试表明,即使使用-Os -flto (针对大小进行优化并启用链接时优化),GCC 5.1.1也不合并由const ref公开的static const unsigned int对象。 坦率地说,如果任何当代的实现都执行了这一困难而晦涩的优化,我将感到惊讶。

(我也猜想在这种用法中“静态”是多余的,但无论如何我都喜欢把它放在那里)

如果有多个翻译单元,这不是多余的,因为否则会违反一个定义规则(ODR)。 但是,作为旁注,很长时间以来,在命名空间范围内, static语法已被认为过时了(考虑使用在C ++ 98中引入的匿名命名空间)。

(响应干杯和hth。-Alf)

如果要确保每个常量只有一个副本,甚至没有副本,则可以使用类模板,如下所示:

没有这种运气。 标准中不能保证模板使用了多少空间。 模板所做的全部保证是,按照潜在规则,仅使用了或似乎要使用的潜在多个副本中的一个。 实际上,这比那更糟,因为至少GCC 5.1.1实际上即使在系统上使用-Os -flto不会删除冗余的static const unsigned int 这意味着使用两个转换单元,即使在仅使用其中一个的情况下,也可以在两个单独的位置找到unsigned int的初始化程序值(所有指针和引用仅引用此位置)。

首先,自C ++ 98起,不建议在名称空间中使用static

D.2 static关键字
在命名空间范围内声明对象时,不建议使用static关键字(请参阅3.3.5)

其次, const暗示C ++本身具有内部链接。

第三,确切答案取决于您使用的编译器和选项。 可以通过编译器/链接器消除重复,特别是如果可以使用LTO(链接时间优化)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM