Как и когда выровнять размер строки кэша?



в отличной ограниченной очереди mpmc Дмитрия Вьюкова, написанной на C++
Смотрите: http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue



Он добавляет некоторые переменные обивка. Я предполагаю, что это должно выровнять его по линии кэша для производительности.



у меня есть несколько вопросов.




  1. почему это делается таким образом?

  2. это портативный метод, который будет
    всегда работают

  3. в каких случаях это лучше всего использовать __attribute__
    ((aligned (64)))
    вместо.


  4. почему заполнение перед указателем буфера помогает с производительностью? не просто указатель загрузки в кэш, так что это действительно только размер указателя?



    static size_t const     cacheline_size = 64;
    typedef char cacheline_pad_t [cacheline_size];

    cacheline_pad_t pad0_;
    cell_t* const buffer_;
    size_t const buffer_mask_;
    cacheline_pad_t pad1_;
    std::atomic<size_t> enqueue_pos_;
    cacheline_pad_t pad2_;
    std::atomic<size_t> dequeue_pos_;
    cacheline_pad_t pad3_;



будет ли эта концепция работать под gcc для кода c?

651   2  

2 ответов:

это сделано таким образом, чтобы разные ядра, изменяющие разные поля, не должны были отскакивать от строки кэша, содержащей их обоих, между их кэшами. Как правило, для доступа процессора к некоторым данным в памяти вся строка кэша, содержащая его, должна находиться в локальном кэше этого процессора. Если он изменяет эти данные, эта запись кэша обычно должна быть единственной копией в любом кэше в системе (эксклюзивный режим в стиле MESI/MOESI протоколы когерентности кэша). Когда отдельные ядра пытаются изменить различные данные, которые живут в одной и той же строке кэша, и, таким образом, тратят время на перемещение всей этой строки вперед и назад, что известно как ложного совместного использования.

в конкретном примере вы даете, одно ядро может быть в очереди запись (чтение (общий)buffer_ и писать (эксклюзив) только enqueue_pos_), а другой извлекает (общий buffer_ и эксклюзивного dequeue_pos_) без остановки ядра в строке кэша, принадлежащей другой.

обивка в начале означает, что buffer_ и buffer_mask_ в конечном итоге на той же строке кэша, а не разделить на две строки и, таким образом, требуя двойной трафик памяти для доступа.

Я не уверен, что эта техника полностью портативна. предполагается, что каждый cacheline_pad_t сам будет выровнен по границе строки кэша размером 64 байта (его размер), и, следовательно, все, что следует за ним, будет на следующей строке кэша. Насколько я знаю, язык C и C++ стандарты требуют этого только от целых структур, чтобы они могли жить в массивах красиво, не нарушая требований выравнивания любого из их членов. (см. комментарии)

The attribute подход будет более специфичным для компилятора, но может сократить размер этой структуры вдвое, поскольку заполнение будет ограничено округлением каждого элемента до полной строки кэша. Это может быть очень полезно, если у вас их много.

то же самое понятие применяется в C как а также C++.

при работе с прерываниями или высокопроизводительными считываниями данных может потребоваться выравнивание по границе строки кэша, которая обычно составляет 64 байта на строку кэша, и они обязательны для использования при работе с межпроцессными сокетами. С межпроцессными сокетами существуют управляющие переменные, которые не могут быть распределены по нескольким строкам кэша или словам DDR RAM, иначе это приведет к тому, что L1, L2 и т. д. или кэши или DDR RAM будут функционировать как фильтр нижних частот и отфильтровывать данные прерывания! ЧТО ЭТО ПЛОХО!!! Это означает, что вы получаете странные ошибки, когда ваш алгоритм хорош, и у него есть потенциал, чтобы заставить вас сойти с ума!

оперативная память DDR почти всегда будет читать в 128-битных словах (DDR RAM Words), что составляет 16 байт, поэтому переменные кольцевого буфера не должны распространяться на несколько слов оперативной памяти DDR. некоторые системы используют 64-разрядные слова DDR RAM, и технически вы можете получить 32-разрядное слово DDR RAM на 16-разрядном процессоре, но в этой ситуации будет использоваться SDRAM.

можно также просто будьте заинтересованы в минимизации количества строк кэша, используемых при чтении данных в высокопроизводительном алгоритме. В моем случае я разработал самый быстрый в мире алгоритм целочисленных строк (на 40% быстрее, чем предыдущий самый быстрый алгоритм), и я работаю над оптимизацией алгоритма Grisu, который является самым быстрым в мире алгоритмом с плавающей запятой. Чтобы напечатать число с плавающей запятой, вы должны напечатать целое число, поэтому для оптимизации Grisu одна оптимизация, которую я реализовал, у меня есть cache-line-выровнял таблицы поиска (LUT) для Grisu ровно в 15 строк кэша, что довольно странно, что он действительно выровнен таким образом. Это принимает лут от .раздел bss (т. е. статическая память) и помещает их в стек (или кучу, но стек более подходит). Я не сравнивал это, но это хорошо, чтобы поднять, и я узнал много об этом, это самый быстрый способ загрузить значения, чтобы загрузить их из i-cache, а не d-cache. Разница в том, что я-кэш только для чтения и имеет гораздо большие строки кэша, потому что это только для чтения (2KB было то, что профессор процитировал меня однажды.). Таким образом, вы на самом деле собираетесь дегригировать свою производительность из индексации массива, а не загружать переменную следующим образом:

int faster_way = 12345678;

в отличие от более медленных образом:

int variables[2] = { 12345678, 123456789};
int slower_way = variables[0];

разница в том, что int variable = 12345678 будет загружаться из строк I-cache путем смещения к переменной в i-cache с самого начала функции, в то время как slower_way = int[0] загрузки из меньших строк D-кэша, использующих гораздо более медленную индексацию массива. Это особенно тонко, как я только что обнаружил, на самом деле замедляет мой и многие другие алгоритм целочисленных строк. Я говорю это, потому что вы можете оптимизировать, выравнивая кэш-данные только для чтения, когда это не так.

как правило, в C++, вы будете использовать

Comments

    Ничего не найдено.