Каков рекомендуемый способ выравнивания памяти в C ++ 11
Я работаю над реализацией кольцевого буфера для одного производителя. У меня есть два требования:
1) Выровняйте один выделенный кучи экземпляр кольцевого буфера в строке кэша.
2) Выровняйте поле в кольцевом буфере по строке кэша (чтобы предотвратить ложное совместное использование).
Мой класс выглядит примерно так:
#define CACHE_LINE_SIZE 64 // To be used later.
template
class RingBuffer { // This needs to be aligned to a cache line.
public:
....
private:
std::atomic publisher_sequence_ ;
int64_t cached_consumer_sequence_;
T* events_;
std::atomic consumer_sequence_; // This needs to be aligned to a cache line.
};
Позвольте мне сначала заняться пунктом 1, т.е.выравнивание выделенного экземпляра кучи класса. Есть несколько способов:
1) Используйте C ++ 11alignas(..)
спецификатор:
template
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)) != 0) {
perror("posix_memalign did not work!");
abort();
}
// Use placement new on a cache aligned buffer.
auto ring_buffer = new(buffer) processor::RingBuffer();
3) Используйте расширение GCC / Clang__attribute__ ((aligned(#)))
template
class RingBuffer {
public:
....
private:
// All the private fields.
} __attribute__ ((aligned(CACHE_LINE_SIZE)));
4) Я пытался использовать стандартизированный C ++ 11aligned_alloc(..)
функция вместоposix_memalign(..)
но GCC 4.8.1 в Ubuntu 12.04 не смог найти определение вstdlib.h
Все это гарантированно делает одно и то же? Моя цель - выравнивание строк кэша, поэтому любой метод, имеющий некоторые ограничения на выравнивание (скажем, двойное слово), не подойдет. Независимость платформы, которая будет указывать на использование стандартизированныхalignas(..)
это вторичная цель.
Мне не ясноalignas(..)
а также__attribute__((aligned(#)))
иметь некоторый предел, который может быть ниже строки кэша на машине. Я могу'это больше не воспроизводится, но при печати адресов, я думаю, я не всегда получал 64-байтовые выровненные адреса сalignas(..)
, Напротивposix_memalign(..)
казалось, всегда работает. Опять же, я не могу больше это воспроизводить, поэтому, возможно, я ошибся.
Вторая цель состоит в том, чтобывыровнять поле внутри класса / структуры в строку кэша. Я делаю это, чтобы предотвратить ложный обмен. Я пробовал следующие способы:
1) Используйте C ++ 11alignas(..)
спецификатор:
template
class RingBuffer { // This needs to be aligned to a cache line.
public:
...
private:
std::atomic publisher_sequence_ ;
int64_t cached_consumer_sequence_;
T* events_;
std::atomic consumer_sequence_ alignas(CACHE_LINE_SIZE);
};
2) Используйте расширение GCC / Clang__attribute__ ((aligned(#)))
template
class RingBuffer { // This needs to be aligned to a cache line.
public:
...
private:
std::atomic publisher_sequence_ ;
int64_t cached_consumer_sequence_;
T* events_;
std::atomic consumer_sequence_ __attribute__ ((aligned (CACHE_LINE_SIZE)));
};
Оба эти метода, кажется, совпадаютconsumer_sequence
по адресу 64 байта после начала объекта, так лиconsumer_sequence
выравнивается ли кеш, зависит от того, выровнен ли сам объект. Вот мой вопрос - есть ли лучшие способы сделать то же самое?
РЕДАКТИРОВАТЬ: Причина, по которой align_alloc не работал на моей машине, заключалась в том, что я работал на eglibc 2.15 (Ubuntu 12.04). Это работало над более поздней версией eglibc.
ОтСтраница man:The function aligned_alloc() was added to glibc in version 2.16
Это делает его довольно бесполезным для меня, так как я не могу требовать такой недавней версии eglibc / glibc.