Ложное совместное использование и выравнивание/дополнение по 128 байт

Проведя некоторое исследование алгоритмов блокировки/ожидания, я наткнулся на проблему ложного обмена. Копание немного больше привело меня к исходному коду Фолли (библиотека Facebook С++) и более конкретно к этому файлу заголовка и определению макроса FOLLY_ALIGN_TO_AVOID_FALSE_SHARING (в настоящее время в строке 130). Что меня больше всего удивило на первый взгляд: значение 128 (т.е.: вместо 64)...

/// An attribute that will cause a variable or field to be aligned so that
/// it doesn't have false sharing with anything at a smaller memory address.
#define FOLLY_ALIGN_TO_AVOID_FALSE_SHARING __attribute__((__aligned__(128)))

AFAIK, блоки кэша на современных процессорах имеют длину 64 байта и фактически, все ресурсы, которые я нашел до сих пор по этому вопросу, включая эту статью от Intel, говорят о 64 байты, выравнивающие и, чтобы помочь работать с ложным совместным использованием.

Тем не менее, люди в Facebook выравнивают и заполняют их члены класса до 128 байтов, когда это необходимо. Затем я узнал начало объяснения чуть выше FOLLY_ALIGN_TO_AVOID_FALSE_SHARING:

enum {
    /// Memory locations on the same cache line are subject to false
    /// sharing, which is very bad for performance.  Microbenchmarks
    /// indicate that pairs of cache lines also see interference under
    /// heavy use of atomic operations (observed for atomic increment on
    /// Sandy Bridge).  See FOLLY_ALIGN_TO_AVOID_FALSE_SHARING
    kFalseSharingRange = 128
};

Пока он дает мне немного больше деталей, я все еще чувствую, что мне нужны некоторые идеи. Мне интересно, как синхронизация смежных строк кэша или любая работа RMW на них могут мешать друг другу при интенсивном использовании атомных операций. Может кто-нибудь, пожалуйста, просветит меня о том, как это может произойти?

Ответ 1

Если вы используете атомарные операции или нет, кэш имеет "кеш-строку", которая является наименьшей единицей, в которой работает кеш. Это зависит от модели процессора от 32 до 128 байтов. Ложное совместное использование - это когда элементы одной и той же строки кэша "разделяются" между разными потоками (которые выполняются на разных процессорах [1]). Когда это произойдет, один процессор, обновляющий "его значение", заставит все остальные процессоры "избавиться от своей копии" этих данных. Это становится хуже в случае атомных операций, потому что для выполнения любой атомной операции процессор, выполняющий операцию, должен будет обеспечить, чтобы все остальные процессоры избавились от "своих копий", прежде чем он сможет обновить значение (чтобы гарантировать, что никакой другой процессор не использует "старое" значение до того, как значение было обновлено) - для этого требуется много сообщений об обслуживании кеша, которые будут распространяться через систему, и процессоры для повторной загрузки значений, которые они ранее имели в кеше.

Итак, с точки зрения производительности, если у вас есть переменные, которые используются одним потоком, разделяйте их на свою собственную строку кеша (в примере в исходном сообщении это считается 128 байтами) путем выравнивания данные к этому значению - это означает, что каждый кусок данных начинается с четной границы линии кэша, и никакой другой процессор не будет "делиться" с одними и теми же данными (если вы действительно не делите данные между потоком, - в этот момент вы должны выполнить соответствующие кеш-обслуживание для обеспечения корректного обновления данных между процессорами)

[1] Или процессорные ядра в современном процессоре с несколькими ядрами. Для простоты я использовал термин "процессор" или "процессоры" для соответствия либо реальным процессорным сокетам, либо процессорным ядрам в одном сокете. Для этого обсуждения различие в значительной степени не имеет значения.

Ответ 2

Похоже, что, хотя Intel использует строки с байтами в 64 байта, существуют различные другие архитектуры, которые используют строки кеша 128 байтов... например:

http://goo.gl/8L6cUl

Power Systems использует строки кэша длиной 128 байт. По сравнению с процессорами Intel (64-байтовые строки кэша) эти большие строки кэша имеют...

Я нашел разбросанные по интернет-заметкам, что другие архитектуры, даже старые, делают то же самое:

http://goo.gl/iNAZlX

Процессор SGI MIPS R10000 на исходном компьютере

Процессор имеет размер кеша в 128 байт.

Скорее всего, программисты Facebook хотели играть в этом безопасно и не хотели иметь большую коллекцию #define/#if на основе архитектуры процессора, рискуя тем, что некоторые более новые процессоры Intel имели линию кэша в 128 байт и никто не помнил, чтобы исправить код.