Выравнивание линии кэша (требуется разъяснение по статье)

недавно я столкнулся с тем, что я думаю, является проблемой ложного обмена в моем приложении, и я посмотрел статья Саттера о том, как выровнять мои данные в кэш-линии. Он предлагает следующий C++ код:

// C++ (using C++0x alignment syntax)
template<typename T>
struct cache_line_storage {
   [[ align(CACHE_LINE_SIZE) ]] T data;
   char pad[ CACHE_LINE_SIZE > sizeof(T)
        ? CACHE_LINE_SIZE - sizeof(T)
        : 1 ];
};

Я вижу, как это будет работать, когда CACHE_LINE_SIZE > sizeof(T) истинно -- The struct cache_line_storage просто заканчивается тем, что занимает одну полную строку кэша памяти. Однако, когда sizeof(T) больше, чем одна строка кэша, я бы подумал, что мы должны заполнить данные CACHE_LINE_SIZE - T % CACHE_LINE_SIZE байт, так что результирующая структура имеет размер, который является интегральным кратным размеру строки кэша. Что не так с моим пониманием? Почему достаточно заполнения 1 байтом?

3 ответов


у вас не может быть массивов размером 0, поэтому для его компиляции требуется 1. Однако текущая черновая версия спецификации говорит, что такое заполнение не требуется; компилятор должен заполнять до выравнивания структуры.

Обратите также внимание, что этот код плохо сформирован, если CACHE_LINE_SIZE меньше, чем alignof(T). Чтобы исправить это, вы, вероятно, должны использовать [[align(CACHE_LINE_SIZE), align(T)]], что обеспечит меньшую расклад не брал.


представьте себе,

#define CACHE_LINE_SIZE 32
sizeof(T) == 48

Теперь рассмотрим, как [[ align(CACHE_LINE_SIZE) ]], работает. например:

[[ align(32) ]] Foo foo;

это sizeof(Foo) == 32n для некоторых n. ie align () будет прокладкой для вас, если необходимо, для таких вещей, как Foo foo[10]; для каждого foo[i] выровнено по запросу.

так, в нашем случае с sizeof(T) == 48, это значит sizeof(cache_line_storage<T>) == 64.

таким образом, выравнивание дает вам дополнение, на которое вы надеялись.

однако, это одна "ошибка" в шаблоне. Рассмотрим этот случай:

#define CACHE_LINE_SIZE 32
sizeof(T) == 32

здесь мы заканчиваем с char pad[1];. Что означает sizeof(cache_line_storage<T>) == 64. Наверное, не то, что ты хочешь!

Я думаю, что шаблон должен быть несколько изменен:

template <typename T, int padding>
struct pad_or_not
{
   T data;
   char pad[padding];
};

// specialize the 0 case
// As it is late, I am SURE I've got the specialization syntax wrong...
template <typename T, int>
struct pad_or_not<0>
{
   T data;
};

template<typename T>
struct cache_line_storage {
   [[ align(CACHE_LINE_SIZE) ]] pad_or_not<T, (sizeof(T) > CACHE_LINE_SIZE ? 0 : CACHE_LINE_SIZE - sizeof(T) ) > data;
};

или что-то в этом роде.


"У вас не может быть массивов размером 0, поэтому для его компиляции требуется 1" - GNU C разрешает массивы с нулевым размером. См. также http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Zero-Length.html