假设您必须实现一个static_vector<T, N>
类,它是一个固定容量容器,完全存在于堆栈中并且从不分配,并且公开std::vector
- 就像接口。 (Boost提供boost::static_vector
。)
考虑到我们必须为N
的最大T
个实例提供未初始化的存储,在设计内部数据布局时可以有多种选择:
单个成员union
:
union U { T _x; };
std::array<U, N> _data;
单std::aligned_storage_t
:
std::aligned_storage_t<sizeof(T) * N, alignof(T)> _data;
std::aligned_storage_t
的数组:
using storage = std::aligned_storage_t<sizeof(T), alignof(T)>;
std::array<storage, N> _data;
无论选择何种选择,创建成员都需要使用"placement new
",访问它们需要reinterpret_cast
的内容。
现在假设我们有两个非常小的static_vector<T, N>
实现:
with_union
:使用&#34;单个成员union
&#34;方法
with_storage
:使用&#34;单std::aligned_storage_t
&#34;方法
让我们使用g++
和clang++
-O3
同时执行以下基准测试。我使用了quick-bench.com for this task:
void escape(void* p) { asm volatile("" : : "g"(p) : "memory"); }
void clobber() { asm volatile("" : : : "memory"); }
template <typename Vector>
void test()
{
for(std::size_t j = 0; j < 10; ++j)
{
clobber();
Vector v;
for(int i = 0; i < 123456; ++i) v.emplace_back(i);
escape(&v);
}
}
(escape
和clobber
取自Chandler Carruth的CppCon 2015演讲:"Tuning C++: Benchmarks, and CPUs, and Compilers! Oh My!")
g++ 7.2
(live here)的结果:clang++ 5.0
(live here)的结果:从结果中可以看出,g++
似乎能够积极优化(向量化)使用&#34;单std::aligned_storage_t
&#的实现34;方法,但不是使用union
的实现。
我的问题是:
标准中是否有任何内容阻止使用union
进行积极优化? (即标准在使用时为编译器授予更多自由度std::aligned_storage_t
- 如果是,为什么?)
这纯粹是一种&#34;实施质量&#34;问题?
答案 0 :(得分:11)
xskxzr是对的,这与this question中的问题相同。从根本上说,gcc在忘记std::array
的数据对齐方面缺少优化机会。 John Zwinck帮助报告了bug 80561。
您可以通过对with_union
进行两次更改之一来验证您的基准测试:
将_data
从std::array<U, N>
更改为U[N]
。表现相同
通过将_data
的实施更改为:
emplace_back()
实际上是对齐的
template <typename... Ts>
T& emplace_back(Ts&&... xs)
{
U* data = static_cast<U*>(__builtin_assume_aligned(_data.data(), alignof(U)));
T* ptr = &data[_size++]._x;
return *(new (ptr) T{std::forward<Ts>(xs)...});
}
使用其他基准测试的这些更改中的任何一项都会让我在WithUnion
和WithAlignedStorage
之间获得可比较的结果。