Ответ 1
Это делается так, что различным ядрам, изменяющим разные поля, не придется отказываться от строки кэша, содержащей обе их между кешами. В общем случае, чтобы процессор мог получить доступ к некоторым данным в памяти, вся строка кэша, содержащая его, должна находиться в локальном кеше процессора. Если он изменяет эти данные, этот элемент кэша обычно должен быть единственной копией в любом кеше в системе (Исключительный режим в протоколах когерентности кеша MESI/MOESI). Когда отдельные ядра пытаются изменить разные данные, которые происходят в одной и той же строке кэша, и, таким образом, тратить время на перемещение всей строки взад и вперед, что называется ложным совместным использованием.
В конкретном примере, который вы даете, одно ядро может включать в себя запись (чтение (общий) buffer_
и запись (только исключение) только enqueue_pos_
), а другой dequeues (общий buffer_
и эксклюзивный dequeue_pos_
) без либо остановка ядра в строке кэша, принадлежащей другой.
Заполнение в начале означает, что buffer_
и buffer_mask_
заканчиваются в одной и той же строке кэша, а не разбиваются на две строки и, следовательно, требуют двойного доступа к трафику памяти.
Я не уверен, что эта техника полностью переносима. Предполагается, что каждый (см. Комментарии)cacheline_pad_t
сам будет выровнен по границе строки кеша на 64 байта (его размер), и, следовательно, все последующее будет в следующей строке кэша. Насколько я знаю, стандарты языка C и С++ требуют только целого ряда структур, чтобы они могли прекрасно жить в массивах, не нарушая требований к выравниванию любого из своих членов.
Подход attribute
был бы более специфичным для компилятора, но мог бы сократить размер этой структуры пополам, поскольку дополнение будет ограничено округлением каждого элемента до полной строки кэша. Это может быть очень полезно, если у них их много.
Такая же концепция применяется как в C, так и в С++.