静态向量内部数据布局 - `union` vs`std :: aligned_storage_t` - 巨大的性能差异

时间:2018-02-06 14:22:33

标签: c++ optimization language-lawyer unions c++17

假设您必须实现一个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);
    }
}

escapeclobber取自Chandler Carruth的CppCon 2015演讲:"Tuning C++: Benchmarks, and CPUs, and Compilers! Oh My!"

g++ results

clang++ results

从结果中可以看出,g++似乎能够积极优化(向量化)使用&#34;单std::aligned_storage_t&#的实现34;方法,但不是使用union的实现。

我的问题是:

  • 标准中是否有任何内容阻止使用union进行积极优化? (即标准在使用时为编译器授予更多自由度std::aligned_storage_t - 如果是,为什么?)

  • 这纯粹是一种&#34;实施质量&#34;问题?

1 个答案:

答案 0 :(得分:11)

xskxzr是对的,这与this question中的问题相同。从根本上说,gcc在忘记std::array的数据对齐方面缺少优化机会。 John Zwinck帮助报告了bug 80561

您可以通过对with_union进行两次更改之一来验证您的基准测试:

  1. _datastd::array<U, N>更改为U[N]。表现相同

  2. 通过将_data的实施更改为:

    ,提醒gcc 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)...});
    }
    
  3. 使用其他基准测试的这些更改中的任何一项都会让我在WithUnionWithAlignedStorage之间获得可比较的结果。