什么是build议的方式来alignment内存在C + + 11

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

1)将一个环形缓冲区的一个堆分配实例与一个caching行alignment。

2)将环形缓冲区中的字段与caching行alignment(以防止错误共享)。

我的课程看起来像这样:

#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. }; 

让我先解决点1,即alignment一个单一堆分配的类实例 。 有几种方法:

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

 template<typename T, uint64_t num_events> class alignas(CACHE_LINE_SIZE) RingBuffer { public: .... private: // All the private fields. }; 

2)使用posix_memalign(..) + placement 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>(); 

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))); 

4)我试图使用C ++ 11标准化的aligned_alloc(..)函数而不是posix_memalign(..)但是Ubuntu 12.04上的GCC 4.8.1找不到stdlib.h的定义

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

我不清楚是否alignas(..)__attribute__((aligned(#)))有一些限制,可能会在机器上的caching线以下。 我不能重现这一点,但打印地址时,我想我并不总是得到与alignas(..) 64个字节alignment的地址。 相反posix_memalign(..)似乎总是工作。 再次,我不能再现这个,所以也许我犯了一个错误。

第二个目标是将类/结构中的字段与高速caching行alignment 。 我正在这样做,以防止虚假分享。 我尝试了以下方法:

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); }; 

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))); }; 

这两个方法似乎将consumer_sequence与对象开始之后的64个字节alignment,所以consumer_sequence是否cachingalignment取决于对象本身是否cachingalignment。 在这里,我的问题是 – 有没有更好的方法来做同样的事情?

编辑 ︰aligned_alloc没有在我的机器上工作的原因是,我在eglibc 2.15(Ubuntu 12.04)。 它在eglibc的更高版本上工作。

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

这对我来说很没用,因为我不能要求eglibc / glibc的最新版本。

不幸的是,我发现最好的是分配额外的空间,然后使用“alignment”部分。 所以RingBuffer new可以请求额外的64个字节,然后返回第一个64字节alignment的部分。 浪费空间,但会给你需要的路线。 您可能需要在返回到实际的分配地址之前设置内存来取消分配它。

 [Memory returned][ptr to start of memory][aligned memory][extra memory] 

(假设没有从RingBufferinheritance)如下所示:

 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); } } 

对于第二个要求RingBuffer的数据成员也是64字节alignment的,为此,如果你知道这个开始是alignment的,你可以用pad来强制alignment数据成员。

你的问题的答案是std :: aligned_storage 。 它可以用于顶级和一个class的个别成员。

经过一些更多的研究,我的想法是:

1)像@TemplateRex指出,似乎没有一个标准的方式来alignment到超过16个字节。 所以,即使我们使用标准化的alignas(..)也没有保证,除非alignment边界小于或等于16字节。 我将不得不validation它在目标平台上按预期工作。

2) __attribute ((aligned(#)))或者alignas(..)不能用来alignment一个堆分配的对象,因为我怀疑ie new()对这些注释没有做任何事情。 他们似乎工作静态对象或堆栈分配与(1)的注意事项。

要么posix_memalign(..) (非标准)或aligned_alloc(..) (标准化,但不能得到它在GCC 4.8.1上工作)+贴装new(..)似乎是解决scheme。 我需要平台独立代码的解决scheme是编译器特定的macros:)

3)结构/类字段的alignment似乎与__attribute ((aligned(#)))alignas()如答案中所述。 再次,我想从(1)关于alignment保证立场的警告。

所以我目前的解决scheme是使用posix_memalign(..) + placement new(..)来alignment我的类的堆分配实例,因为我现在的目标平台只是Linux。 我也使用alignas(..)来alignment字段,因为它是标准化的,至less可以在Clang和GCC上使用。 如果有更好的答案,我会很乐意改变它。

我不知道是否将内存分配到一个新的操作符是最好的方法,但它确实非常简单!

这是在GCC 6.1.0中的线程清理器通过的方式

 #define ALIGNED(x) __attribute__((aligned(x))) static char myarray[sizeof(myClass)] ALIGNED(64) ; var = new(myarray) myClass; 

那么,在sanitizer_common / sanitizer_internal_defs.h,它也写

 // Please only use the ALIGNED macro before the type. // Using ALIGNED after the variable declaration is not portable! 

所以我不知道为什么在variables声明之后使用了ALIGNED。 但是这是另外一个故事。