什么是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。 但是这是另外一个故事。