Как работают временные инструкции?

Я читаю то, что каждый программист должен знать о памяти pdf Ульриха Дреппера. В начале части 6 есть фрагмент кода:

#include <emmintrin.h>
void setbytes(char *p, int c)
{
    __m128i i = _mm_set_epi8(c, c, c, c,
    c, c, c, c,
    c, c, c, c,
    c, c, c, c);
    _mm_stream_si128((__m128i *)&p[0], i);
    _mm_stream_si128((__m128i *)&p[16], i);
    _mm_stream_si128((__m128i *)&p[32], i);
    _mm_stream_si128((__m128i *)&p[48], i);
}

С таким комментарием прямо под ним:

Принимая указатель p правильно выровнен, вызов этой функции установит все байты адресной строки кэша в c, Логика объединения записи увидит четыре сгенерированные инструкции movntdq и выдаст команду записи в память только после выполнения последней инструкции. Подводя итог, можно сказать, что эта кодовая последовательность не только предотвращает чтение строки кэша перед ее записью, но также предотвращает загрязнение кэша данными, которые могут вскоре не понадобиться.

Что меня беспокоит, так это то, что в комментарии к функции написано, что она "установит все байты строки адресуемого кэша в c", но из того, что я понимаю о потоке, они обходят кэши - нет ни чтения, ни записи в кэш. Как этот код получит доступ к любой строке кэша? Второй выделенный жирным шрифтом фрагмент говорит о том, что функция "избегает чтения строки кэша до ее записи". Как указано выше, я не вижу, как и когда кеши записываются. Кроме того, любая запись в кэш должна предшествовать записи в кэш? Может ли кто-нибудь прояснить мне этот вопрос?

3 ответа

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

Когда вы пишете в память, хранилища группируются в буферах хранилища. Обычно после заполнения буфера он сбрасывается в кэш / память. Обратите внимание, что количество буферов хранилища обычно невелико (~4). Последовательная запись по адресам будет использовать один и тот же буфер хранилища.

Потоковое чтение / запись с временными подсказками обычно используется для уменьшения загрязнения кэша (часто с использованием памяти WC). Идея состоит в том, что небольшой набор строк кэша зарезервирован на процессоре для использования этими инструкциями. Вместо загрузки строки кэша в основные кэши, она загружается в этот меньший кэш.

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

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

Обратите внимание, что если записанная строка кэша уже находится в основных кэшах, вышеуказанный метод также обновит их.

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

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

Обычно размер временного кэша составляет порядка 4-8 строк.

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

Я думаю, что это отчасти вопрос терминологии: отрывок, который вы цитируете из статьи Ульриха Дреппера, не говорит о кэшированных данных. Он просто использует термин "строка кэша" для выровненного блока размером 64B.

Это нормально и особенно полезно, когда речь идет о диапазоне оборудования с различными размерами строк кэша. (Ранее в процессорах x86, совсем недавно, в PIII, было 32 КБ строк кэша, поэтому использование этой терминологии позволяет избежать жесткого кодирования решения о разработке микроархива при обсуждении.)

Строка кэша данных по-прежнему является строкой кэша, даже если в настоящее время она не активна ни в одном кеше.

У меня нет ссылок под пальцами, чтобы доказать, что я говорю, но я понимаю следующее: единственная единица передачи по шине памяти - это строки кэша, идут ли они в кеш или в какие-то специальные регистры. Действительно, вставленный вами код заполняет строку кэша, но это специальная строка кэша, которая не находится в кэше. Как только все байты этой строки кэша были изменены, строка кэша отправляется непосредственно в память, не проходя через кэш.

Другие вопросы по тегам