odr使用的空类的优化
optimization for ODR-used empty classes
现在的许多c++代码都倾向于在很大程度上加载模板。它们是库:STL、Boost。精神,提振。MPL等等等。它们鼓励用户在表单中声明功能对象struct S { /* presence of non-virtual member functions and operators, but absense of non-static data members or non-empty base classes */ }; S const s{};
。它们中的大多数是无状态的(即static_assert(std::is_empty< S >{});
持有)。对于它们中的那些,这是odr使用的,不管它们的空data
部分的文件增长1字节(sizeof(S) == 1
为空类型S
,因为所有的地址为必然分配的对象应该是不同的)。即使在Boost的简单语法中。精神有很多这样的odr使用的空类。但是为他们保留空间是绝对没有意义的。
我尝试使用以下代码(-Ofast
)在coliru上测试clang
:
#include <utility>
#include <type_traits>
#include <cstdlib>
#include <cassert>
template< std::size_t index >
struct S {};
namespace
{
template< std::size_t index >
S< index > value = {};
}
template< typename lhs, typename rhs >
std::ptrdiff_t
diff(lhs & l, rhs & r)
{
return (static_cast< char * >(static_cast< void * >(&r)) - static_cast< char * >(static_cast< void * >(&l)));
}
template< std::size_t base, std::size_t ...indices >
std::ptrdiff_t
bss_check(std::index_sequence< indices... >)
{
return (diff(value< (base + indices) >, value< (base + indices + 1) >) + ...);
}
template< std::size_t size, std::size_t base >
bool
enumerate()
{
return (bss_check< base >(std::make_index_sequence< size >{}) + 1 == size);
}
template< std::size_t size, std::size_t ...bases >
bool
expand(std::index_sequence< bases... >)
{
return (enumerate< size, (bases * size) >() && ...);
}
template< std::size_t size = 100, std::size_t count = size >
bool
check()
{
return expand< size >(std::make_index_sequence< count >{});
}
int
main()
{
static_assert(std::is_empty< S< 0 > >{});
assert((check< DIM >()));
return EXIT_SUCCESS;
}
并获得结果(size
实用程序对DIM == 100
的输出,即100 * 100个类):
text data bss dec hex filename
112724 10612 4 123340 1e1cc ./a.out
如果我将diff(lhs & l, rhs & r)
的签名更改为diff(lhs l, rhs r)
以抑制odr使用,那么结果是:
text data bss dec hex filename
69140 608 8 69756 1107c ./a.out
几乎等于(data
节只关心)assert((check< DIM >()));
行的简单注释情况(text
节的主要部分是可预测的dce优化):
text data bss dec hex filename
1451 600 8 2059 80b ./a.out
因此,我得出结论,对于使用odr的空类没有优化。
对于显式指定的模板参数,可以使用简单的类型过滤器:
template< typename type >
using ref_or_value = std::conditional_t< std::is_empty< std::decay_t< type > >{}, std::decay_t< type >, type && >;
但在我看来,没有简单的解决方法来推导模板类型。
在现代编译器中是否有隐含的上述优化?是,如何启用?如果没有,是否有一种技术可以实现当前的预期行为?
我知道有时对象的地址会发出很多杂音,但在上述情况下并非如此。
我认为像变量或类型的属性(例如[[immaterial]]
)将是方便的。也许这样的属性(用于类)应该拒绝获得带属性类实例的地址的可能性(编译时硬错误),或者操作符的地址&
应该返回无意义的值(实现定义)。
c++ 17将添加内联变量来帮助解决N4424中解释的一些问题。它还解释了一些变通方法。对于全局函数对象,你可以这样定义它们:
// Sum function object
struct sum_f
{
template<class T, class U>
auto operator()(T x, U y) const
{
return x+y;
}
};
template<class T>
struct static_const_storage
{
static constexpr T value = T();
};
template<class T>
constexpr T static_const_storage<T>::value;
template<class T>
constexpr const T& static_const()
{
return static_const_storage<T>::value;
}
static constexpr auto& sum = static_const<sum_f>();
这使得sum
函数对象在翻译单元中是唯一的,从而避免了膨胀和ODR冲突。然而,这种解决方法并不真正适用于模板变量,最好避免使用它们(如果你担心可执行文件膨胀),直到我们在c++ 17中得到内联变量。
- 空基优化子对象的地址
- 关闭||运算符优化
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- 返回值优化:显式移动还是隐式
- 人脸跟踪arduino代码的优化
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 纯函数,为什么没有优化
- 为什么大多数 pair 实现默认不使用压缩(空基优化)?
- 如何以优化的方式同时迭代两个间距不相等的数组
- 小字符串优化(调试与发布模式)
- 浮点定向舍入和优化
- Visual Studio 调试优化如何工作?
- 为什么开关的优化方式与 c/c++ 中的链接不同?
- 线性优化目标函数中的绝对值
- GCC 会优化内联访问器吗?
- gcc 如何优化此循环?
- 如何防止 CUDA-GDB 中的<优化输出>值
- 为什么我的程序在 O0 和 O2 的优化级别返回不同的结果
- 这个C++编译器优化(在自身的实例上调用对象自己的构造函数)的名称是什么,它是如何工作的?
- odr使用的空类的优化