constexpr 值会导致二进制大小增加吗?
Do constexpr values cause binary size to increase?
我知道像下面这样的模板化类型对编译后的二进制大小没有任何影响:
template<auto value>
struct ValueHolder{};
我正在制作一个程序,它将使用很多这样的包装类型,我不认为我想因为这个原因使用 integral_constant
s,因为它们有一个 ::value
成员。我可以逃脱更像是:
template<typename ValHolder>
struct Deducer;
template<auto value>
struct Deducer<ValueHolder<value>> {
using Output = ValueHolder<value+1>;
};
但这肯定需要更多的工作,所以我想确保我没有白做。请注意,我们正在谈论大量这样的值(我会解释,但我不想离题太远;我可能会收到更多关于“我应该做这个项目”的评论,而不是问题!) .
所以问题是:[static] constexpr 值在编译的二进制文件中是否采用任何大小,或者这些值是否在编译时被替换,就好像它们是按字面输入的一样?我很确定它们确实占用了二进制文件的大小,但我并不肯定。
我在 godbolt
做了一个小测试,并排查看 constexpr 与非 constexpr 数组的组装,一切看起来都与我非常相似:https://godbolt.org/z/9hecfq
int main()
{
// Non-constexpr large array
size_t arr[0xFFFF] = {};
// Constexpr large array
constexpr size_t cArr[0xFFF] = {};
// Just to avoid unused variable optimizations / warnings
cout << arr[0] << cArr[0] << endl;
return 0;
}
这完全取决于:
- 编译器在多大程度上想要优化变量。
- 你如何使用变量。
考虑您发布的代码。您创建了一个 constexpr
数组。由于这是一个数组,因此使用运行时值对其进行索引是 100% 合法的。这将要求编译器发出访问该索引处数组的代码,这将要求该数组实际上 exist 在内存中。所以如果你这样使用它,它必须有存储空间。
但是,由于您的代码仅使用常量表达式索引对该数组进行索引,因此想要考虑比 -O0
多一点的编译器会意识到它知道中所有元素的值那个数组。所以它确切地知道 cArr[0]
是什么。这意味着编译器可以将该表达式转换为正确的值,而忽略 cArr
存在。
这样的编译器可以对 arr
做同样的事情,顺便说一句;它不必是编译器检测 no-op.
的常量表达式
此外,请注意,由于两个数组都是 non-static,因此它们都不会占用“在编译的二进制文件中”的存储空间。如果需要它们的运行时存储,它将是堆栈 space,而不是可执行文件 space。
从广义上讲,如果您执行某些需要它占用存储空间的操作,constexpr
变量将在任何合理的优化级别占用存储空间。这可能与将它传递给一个 (un-inlined) 函数一样无害,该函数通过 const&
.
获取参数
询问您的链接器 :) C++ 标准中没有任何内容与答案有任何关系。因此,您绝对肯定地必须在发布模式下构建您的代码,并检查 在特定的使用场景中 它是否确实增加了大小。
您在项目的其他平台、不同编译器 (1)、其他编译选项、其他模块 added/removed 上获得的任何一般结果,甚至对代码的任何更改,都不会有很大的相关性。
您有一个特定问题,该问题取决于很多因素,因此恕我直言,笼统的答案毫无用处。
但此外,如果您真正关心二进制文件的大小,那么它应该已经在您的test/benchmark套件中,当事情发展时,您应该让集成构建失败什么时候不应该,等等。没有测量和没有自动化是表面证据表明你实际上并不关心。
因此,由于您可能确实关心二进制大小,只需编写您想到的代码并在 CI 仪表板中查看二进制大小指标。哦,你没有吗?好吧,这是你继续之前要做的第一件事。我是认真的。
(1):相同的编译器 = 相同的二进制文件。你说我疯了?不,它咬我的次数太多了。如果编译器二进制文件不同(时间戳除外),则它不是同一个编译器,故事结束。
我知道像下面这样的模板化类型对编译后的二进制大小没有任何影响:
template<auto value>
struct ValueHolder{};
我正在制作一个程序,它将使用很多这样的包装类型,我不认为我想因为这个原因使用 integral_constant
s,因为它们有一个 ::value
成员。我可以逃脱更像是:
template<typename ValHolder>
struct Deducer;
template<auto value>
struct Deducer<ValueHolder<value>> {
using Output = ValueHolder<value+1>;
};
但这肯定需要更多的工作,所以我想确保我没有白做。请注意,我们正在谈论大量这样的值(我会解释,但我不想离题太远;我可能会收到更多关于“我应该做这个项目”的评论,而不是问题!) .
所以问题是:[static] constexpr 值在编译的二进制文件中是否采用任何大小,或者这些值是否在编译时被替换,就好像它们是按字面输入的一样?我很确定它们确实占用了二进制文件的大小,但我并不肯定。
我在 godbolt
做了一个小测试,并排查看 constexpr 与非 constexpr 数组的组装,一切看起来都与我非常相似:https://godbolt.org/z/9hecfq
int main()
{
// Non-constexpr large array
size_t arr[0xFFFF] = {};
// Constexpr large array
constexpr size_t cArr[0xFFF] = {};
// Just to avoid unused variable optimizations / warnings
cout << arr[0] << cArr[0] << endl;
return 0;
}
这完全取决于:
- 编译器在多大程度上想要优化变量。
- 你如何使用变量。
考虑您发布的代码。您创建了一个 constexpr
数组。由于这是一个数组,因此使用运行时值对其进行索引是 100% 合法的。这将要求编译器发出访问该索引处数组的代码,这将要求该数组实际上 exist 在内存中。所以如果你这样使用它,它必须有存储空间。
但是,由于您的代码仅使用常量表达式索引对该数组进行索引,因此想要考虑比 -O0
多一点的编译器会意识到它知道中所有元素的值那个数组。所以它确切地知道 cArr[0]
是什么。这意味着编译器可以将该表达式转换为正确的值,而忽略 cArr
存在。
这样的编译器可以对 arr
做同样的事情,顺便说一句;它不必是编译器检测 no-op.
此外,请注意,由于两个数组都是 non-static,因此它们都不会占用“在编译的二进制文件中”的存储空间。如果需要它们的运行时存储,它将是堆栈 space,而不是可执行文件 space。
从广义上讲,如果您执行某些需要它占用存储空间的操作,constexpr
变量将在任何合理的优化级别占用存储空间。这可能与将它传递给一个 (un-inlined) 函数一样无害,该函数通过 const&
.
询问您的链接器 :) C++ 标准中没有任何内容与答案有任何关系。因此,您绝对肯定地必须在发布模式下构建您的代码,并检查 在特定的使用场景中 它是否确实增加了大小。
您在项目的其他平台、不同编译器 (1)、其他编译选项、其他模块 added/removed 上获得的任何一般结果,甚至对代码的任何更改,都不会有很大的相关性。
您有一个特定问题,该问题取决于很多因素,因此恕我直言,笼统的答案毫无用处。
但此外,如果您真正关心二进制文件的大小,那么它应该已经在您的test/benchmark套件中,当事情发展时,您应该让集成构建失败什么时候不应该,等等。没有测量和没有自动化是表面证据表明你实际上并不关心。
因此,由于您可能确实关心二进制大小,只需编写您想到的代码并在 CI 仪表板中查看二进制大小指标。哦,你没有吗?好吧,这是你继续之前要做的第一件事。我是认真的。
(1):相同的编译器 = 相同的二进制文件。你说我疯了?不,它咬我的次数太多了。如果编译器二进制文件不同(时间戳除外),则它不是同一个编译器,故事结束。