在C++ 11中对齐内存的推荐方法是什么?

Raj*_*jiv 66 c++ memory-alignment c++11

我正在研究单个生产者单个消费者环缓冲区实现.我有两个要求:

1)将单个堆分配的环形缓冲区实例与高速缓存行对齐.

2)将环形缓冲区内的字段与高速缓存行对齐(以防止错误共享).

我的班级看起来像:

#define CACHE_LINE_SIZE 64  // To be used later.

template<typename T, uint64_t num_events>
class RingBuffer {  // This needs to be aligned to a cache line.
public:
  ....

private:
  std::atomic<int64_t> publisher_sequence_ ;
  int64_t cached_consumer_sequence_;
  T* events_;
  std::atomic<int64_t> consumer_sequence_;  // This needs to be aligned to a cache line.

};
Run Code Online (Sandbox Code Playgroud)

让我首先解决第1点,即对齐单个堆分配的类实例.有几种方法:

1)使用c ++ 11 alignas(..)说明符:

template<typename T, uint64_t num_events>
class alignas(CACHE_LINE_SIZE) RingBuffer {
public:
  ....

private:
  // All the private fields.

};
Run Code Online (Sandbox Code Playgroud)

2)使用posix_memalign(..)+放置new(..)而不改变类定义.这不受平台独立的影响:

 void* buffer;
 if (posix_memalign(&buffer, 64, sizeof(processor::RingBuffer<int, kRingBufferSize>)) != 0) {
   perror("posix_memalign did not work!");
   abort();
 }
 // Use placement new on a cache aligned buffer.
 auto ring_buffer = new(buffer) processor::RingBuffer<int, kRingBufferSize>();
Run Code Online (Sandbox Code Playgroud)

3)使用GCC/Clang扩展 __attribute__ ((aligned(#)))

template<typename T, uint64_t num_events>
class RingBuffer {
public:
  ....

private:
  // All the private fields.

} __attribute__ ((aligned(CACHE_LINE_SIZE)));
Run Code Online (Sandbox Code Playgroud)

4)我尝试使用C++ 11标准化aligned_alloc(..)函数代替posix_memalign(..)但是Ubuntu 12.04上的GCC 4.8.1无法找到stdlib.h

所有这些都保证做同样的事情吗?我的目标是缓存行对齐,所以任何对齐都有限制的方法(比如双字)都行不通.指向使用标准化的平台独立性alignas(..)是次要目标.

我不是是否清晰alignas(..),并__attribute__((aligned(#)))有一定的限额,这可能是本机上的高速缓存线以下.我不能再重现这一点,但在打印地址时,我认为我并不总是得到64字节对齐的地址alignas(..).相反,posix_memalign(..)似乎总是有效.我再也无法重现这一点,所以也许我犯了一个错误.

第二个目标是将类/结构中的字段与高速缓存行对齐.我这样做是为了防止误共享.我尝试了以下方法:

1)使用C++ 11 alignas(..)说明符:

template<typename T, uint64_t num_events>
class RingBuffer {  // This needs to be aligned to a cache line.
  public:
  ...
  private:
    std::atomic<int64_t> publisher_sequence_ ;
    int64_t cached_consumer_sequence_;
    T* events_;
    std::atomic<int64_t> consumer_sequence_ alignas(CACHE_LINE_SIZE);
};
Run Code Online (Sandbox Code Playgroud)

2)使用GCC/Clang扩展 __attribute__ ((aligned(#)))

template<typename T, uint64_t num_events>
class RingBuffer {  // This needs to be aligned to a cache line.
  public:
  ...
  private:
    std::atomic<int64_t> publisher_sequence_ ;
    int64_t cached_consumer_sequence_;
    T* events_;
    std::atomic<int64_t> consumer_sequence_ __attribute__ ((aligned (CACHE_LINE_SIZE)));
};
Run Code Online (Sandbox Code Playgroud)

这两种方法似乎都consumer_sequence在对象开始后对应一个64字节的地址,因此consumer_sequence缓存是否对齐取决于对象本身是否与缓存对齐.我的问题是 - 有没有更好的方法来做同样的事情?

编辑: 对齐我没有在我的机器上工作的原因是我在eglibc 2.15(Ubuntu 12.04).它适用于eglibc的更高版本.

手册页:The function aligned_alloc() was added to glibc in version 2.16.

这对我来说非常无用,因为我不能要求这样的最新版本的eglibc/glibc.

Gle*_*aum 30

不幸的是,我发现最好的是分配额外的空间,然后使用"对齐"部分.因此,RingBuffer new可以请求额外的64个字节,然后返回其中第一个64字节对齐的部分.它浪费了空间,但会给你所需的对齐.您可能需要在返回到实际的alloc地址之前设置内存以取消分配它.

[Memory returned][ptr to start of memory][aligned memory][extra memory]
Run Code Online (Sandbox Code Playgroud)

(假设没有来自RingBuffer的继承)类似于:

void * RingBuffer::operator new(size_t request)
{
     static const size_t ptr_alloc = sizeof(void *);
     static const size_t align_size = 64;
     static const size_t request_size = sizeof(RingBuffer)+align_size;
     static const size_t needed = ptr_alloc+request_size;

     void * alloc = ::operator new(needed);
     void *ptr = std::align(align_size, sizeof(RingBuffer),
                          alloc+ptr_alloc, request_size);

     ((void **)ptr)[-1] = alloc; // save for delete calls to use
     return ptr;  
}

void RingBuffer::operator delete(void * ptr)
{
    if (ptr) // 0 is valid, but a noop, so prevent passing negative memory
    {
           void * alloc = ((void **)ptr)[-1];
           ::operator delete (alloc);
    }
}
Run Code Online (Sandbox Code Playgroud)

对于具有RingBuffer64字节对齐的数据成员的第二个要求,如果您知道this对齐的开头,则可以填充以强制对齐数据成员.


rub*_*nvb 9

您的问题的答案是std :: aligned_storage.它可以用于顶级和类的个别成员.

  • 但它有与alignas类似的限制(直到c ++ 17高达16字节/平台相关限制) (3认同)