确定C ++中最大可能的对齐方式

时间:2009-10-06 19:44:06

标签: c++ alignment c++11

是否有便携式方式来确定任何类型的最大可能对齐方式是什么?

例如在x86上,SSE指令需要16字节对齐,但据我所知,没有指令需要更多,因此任何类型都可以安全地存储到16字节对齐的缓冲区中。

我需要创建一个缓冲区(例如char数组),我可以在其中编写任意类型的对象,因此我需要能够依赖缓冲区的开头进行对齐。

如果所有其他方法都失败了,我知道分配带new的char数组可以保证最大对齐,但使用TR1 / C ++ 0x模板alignment_ofaligned_storage,我想知道是否可以在我的缓冲区类中就地创建缓冲区,而不是要求动态分配的数组的额外指针间接。

想法?

我意识到有很多选项可以确定一组有界类型的最大对齐:一个联合,或者只是来自TR1的alignment_of,但我的问题是这些类型是无界的。我事先并不知道哪些对象必须存储在缓冲区中。

6 个答案:

答案 0 :(得分:12)

在C ++ 11中,标题cstddef中定义的std :: max_align_t是一种POD类型,其对齐要求至少与每个标量类型的对齐要求一样严格(大)。

使用新的alignof运算符,就像alignof(std::max_align_t)

一样简单

答案 1 :(得分:10)

在C ++ 0x中,Align std::aligned_storage<Len, Align>模板参数的默认参数为“default-alignment”,定义为(N3225§20.7.6.6表56):

  

对于任何大小不超过Len的C ++对象类型,default-alignment的值应该是最严格的对齐要求。

目前尚不清楚SSE类型是否会被视为“C ++对象类型”。

默认参数不是TR1 aligned_storage的一部分;它是为C ++ 0x添加的。

答案 2 :(得分:5)

缺少一些所有编译器都忠实地支持所有架构的maximally_aligned_t类型,我不知道如何在编译时解决这个问题。如你所说,潜在类型的集合是无限的。额外的指针间接是真的那么重要吗?

答案 3 :(得分:5)

不幸的是,确保最大对齐比应该更加困难,并且没有保证解决方案AFAIK。来自GotW博客(Fast Pimpl article):

union max_align {
  short       dummy0;
  long        dummy1;
  double      dummy2;
  long double dummy3;
  void*       dummy4;
  /*...and pointers to functions, pointers to
       member functions, pointers to member data,
       pointers to classes, eye of newt, ...*/
};

union {
  max_align m;
  char x_[sizeofx];
};
  

这不能保证完全   便携式,但在实践中它很接近   因为很少或没有   系统不适用于此   预期

这就是我所知道的最接近的'黑客'。

我个人使用另一种方法进行超快速分配。请注意,这是邪恶的,但我在光线追踪领域工作,速度是最好的质量衡量标准之一,我们每天都会编码。它涉及使用具有预分配内存的堆分配器,其工作方式类似于本地堆栈(仅在分配时递增指针并在解除分配时递减1)。

我特别将它用于Pimpls。但是,仅仅拥有分配器是不够的;为了使这样的分配器工作,我们必须假设一个类Foo的内存在构造函数中分配,同样的内存同样只在析构函数中释放,并且Foo本身是在堆栈上创建的。为了安全起见,我需要一个函数来查看类的'this'指针是否在本地堆栈上,以确定我们是否可以使用我们的超快速堆栈堆栈分配器。为此我们必须研究特定于操作系统的解决方案:我使用TIBsTEBs用于Win32 / Win64,我的同事找到了Linux和Mac OS X的解决方案。

结果,在研究了特定于OS的方法来检测堆栈范围,对齐要求以及进行大量测试和分析之后,结果是一个分配器,它可以根据我们的滴答计数器基准在4个时钟周期内分配内存。反对malloc / operator new约400个周期(我们的测试涉及线程争用,因此在单线程情况下malloc可能比这个快一点,可能是几百个周期)。我们添加了一个每线程堆栈并检测到正在使用哪个线程,这将时间增加到大约12个周期,尽管客户端可以跟踪线程分配器以获得4个周期的分配。它消除了地图上基于内存分配的热点。

虽然您不必经历所有麻烦,但编写快速分配器可能更容易且更普遍适用(例如:允许在运行时确定分配/解除分配的内存量),而不是{{ 1}}这里。 max_align很容易使用,但是如果你的内存分配速度很快(假设你已经分析了你的代码并在malloc / free / operator new / delete中找到热点,主要贡献者在你的代码中)有控制权,编写自己的分配器可以真正发挥作用。

答案 4 :(得分:1)

分配对齐的内存比看起来更复杂 - 例如参见 Implementation of aligned memory allocation

答案 5 :(得分:-2)

这就是我正在使用的。除此之外,如果你正在分配内存,那么长度大于或等于max_alignment的新的()数组char将与max_alignment对齐,这样你就可以使用索引到该数组中来获得对齐的地址。

enum {
            max_alignment = boost::mpl::deref<
                boost::mpl::max_element<
                        boost::mpl::vector<
                            boost::mpl::int_<boost::alignment_of<signed char>::value>::type,
                            boost::mpl::int_<boost::alignment_of<short int>::value>::type,
                            boost::mpl::int_<boost::alignment_of<int>::value>::type,                                boost::mpl::int_<boost::alignment_of<long int>::value>::type,
                            boost::mpl::int_<boost::alignment_of<float>::value>::type,
                            boost::mpl::int_<boost::alignment_of<double>::value>::type,
                            boost::mpl::int_<boost::alignment_of<long double>::value>::type,
                            boost::mpl::int_<boost::alignment_of<void*>::value>::type
                        >::type
                    >::type
                >::type::value
            };
        }