gpt4 book ai didi

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

转载 作者:IT老高 更新时间:2023-10-28 12:12:49 25 4
gpt4 key购买 nike

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

  1. 将单个堆分配的环形缓冲区实例与缓存行对齐。
  2. 将环形缓冲区中的字段与缓存行对齐(以防止错误共享)。

我的类(class)看起来像:

#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 点,即对齐类的单个堆分配实例。有几种方法:

  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(..) + 放置 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

    中找不到定义

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

我不清楚 alignas(..)__attribute__((aligned(#))) 是否有一些限制,可能低于缓存行机器。我无法再重现这一点,但在打印地址时,我认为我并不总是使用 alignas(..) 获得 64 字节对齐的地址。相反 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);
    };
  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 个字节的地址,因此 consumer_sequence 是否缓存对齐取决于对象本身是否缓存对齐.我的问题是 - 有没有更好的方法来做同样的事情?

编辑:

aligned_alloc 在我的机器上不起作用的原因是我使用的是 eglibc 2.15 (Ubuntu 12.04)。它适用于更高版本的 eglibc。

来自 man page : 函数 aligned_alloc() 在 2.16 版本中被添加到 glibc

这对我来说毫无用处,因为我不能需要这样一个最新版本的 eglibc/glibc。

最佳答案

不幸的是,我发现最好的方法是分配额外的空间,然后使用“对齐”部分。所以 RingBuffer new 可以请求额外的 64 字节,然后返回前 64 字节对齐的部分。它会浪费空间,但会提供您需要的对齐方式。您可能需要在返回实际分配地址之前设置内存以取消分配它。

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

(假设没有继承自 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);
}
}

对于第二个要求 RingBuffer 的数据成员也是 64 字节对齐的,如果你知道 this 的开头是对齐的,你可以填充到强制对齐数据成员。

关于c++ - 在 C++11 中对齐内存的推荐方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20791428/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com