我正在开发一个单生产者单消费者环形缓冲区实现。我有两个要求:
- 将单个堆分配的环形缓冲区实例对齐到缓存行。
- 将环形缓冲区内的字段对齐到缓存行(以防止伪共享)。
我的类大致如下:
#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.
};
首先,让我解决第一个问题,即对类的单个堆分配实例进行对齐。有几种方法:
Use the c++ 11
alignas(..)
specifier:template<typename T, uint64_t num_events> class alignas(CACHE_LINE_SIZE) RingBuffer { public: .... private: // All the private fields. };
Use
posix_memalign(..)
+ placementnew(..)
without altering the class definition. This suffers from not being platform independent: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>();
Use the GCC/Clang extension
__attribute__ ((aligned(#)))
template<typename T, uint64_t num_events> class RingBuffer { public: .... private: // All the private fields. } __attribute__ ((aligned(CACHE_LINE_SIZE)));
I tried to use the C++ 11 standardized
aligned_alloc(..)
function instead ofposix_memalign(..)
but GCC 4.8.1 on Ubuntu 12.04 could not find the definition instdlib.h
这些方法是否都能保证达到同样的效果?我的目标是缓存行对齐,因此任何在对齐方面有限制(比如双字)的方法都不会起作用。平台独立性是使用标准化的alignas(..)
的次要目标。
我不清楚alignas(..)
和__attribute__((aligned(#)))
是否存在某些限制,可能低于机器上的缓存行。我无法再重现这个问题,但在打印地址时,我认为alignas(..)
并不总是获得64字节对齐的地址。相反,posix_memalign(..)
似乎总是有效的。但我不能再次重现这个问题,所以可能是我弄错了。
第二个目标是将类/结构体中的字段对齐到缓存行。我这样做是为了防止虚假共享。我尝试过以下方式:
Use the C++ 11
alignas(..)
specifier: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); };
Use the GCC/Clang extension
__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字节地址对齐,因此consumer_sequence
是否缓存对齐取决于对象本身是否缓存对齐。我的问题是 - 是否有更好的方法来做到同样的效果?编辑:原因是
aligned_alloc
在我的机器上无法工作,因为我使用的是eglibc 2.15(Ubuntu 12.04)。它在较新版本的eglibc上运行良好。从man页面可以看出:函数
aligned_alloc()
是在glibc 2.16中添加的。这使得它对我来说几乎没有用处,因为我不能要求使用如此新的eglibc / glibc版本。