Если я не использую ограждения, сколько времени может потребоваться ядру, чтобы увидеть записи другого ядра?

Я пытался погуглить свой вопрос, но, честно говоря, не знаю, как его кратко сформулировать.

Предположим, у меня есть два потока в многоядерной системе Intel. Эти потоки работают на одном узле NUMA. Предположим, поток 1 записывает в X один раз, а затем читает его только изредка, продвигаясь вперед. Предположим далее, что, помимо прочего, поток 2 постоянно читает X. Если я не использую забор памяти, сколько времени может пройти между потоком 1, записывающим X, и потоком 2, который увидит обновленное значение?

Я понимаю, что запись X пойдет в буфер хранилища, а оттуда в кеш, после чего сработает MESIF, и поток 2 увидит обновленное значение через QPI. (Или, по крайней мере, это то, что я почерпнул). Я предполагаю, что буфер хранилища будет записан в кеш либо на ограждении магазина, либо если эту запись буфера хранилища необходимо повторно использовать, но я не знаю, что буферы хранилища выделяются для записи.

В конечном итоге вопрос, на который я пытаюсь ответить для себя, заключается в том, может ли поток 2 не видеть запись потока 1 в течение нескольких секунд в довольно сложном приложении, которое выполняет другую работу.


person Cube Fan    schedule 11.07.2018    source источник
comment
Если два потока работают на одном узле NUMA, QPI не будет задействован.   -  person Hadi Brais    schedule 12.07.2018


Ответы (1)


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

Буфер хранилища всегда пытается зафиксировать списанные (известные неспекулятивные) хранилища в кэш L1d как можно быстрее. Кэш согласован 1, что делает их глобально видимыми благодаря MESI / MESIF / MOESI. буфер хранилища не предназначен в качестве правильного кеша или буфера объединения записи (хотя он может объединять последовательные хранилища в одну и ту же строку кеша), поэтому ему необходимо опустошить себя, чтобы освободить место для новых хранилищ. В отличие от кеша, он хочет оставаться пустым, а не полным.

Примечание 1: не только x86; все многоядерные системы любого ISA, в которых мы можем запускать один экземпляр Linux на его ядрах, обязательно имеют согласованный кэш; Linux полагается на volatile в своих созданных вручную атомах, чтобы сделать данные видимыми. И аналогично, C ++ std::atomic операции загрузки / сохранения с mo_relaxed - это просто загрузка и сохранение asm на всех обычных процессорах, которые зависят от оборудования для видимости между ядрами, а не вручную. Когда использовать volatile с многопоточностью? объясняет th . Существуют кластеры или гибридные платы ARM микроконтроллер + DSP с некогерентной общей памятью, но мы не запускаем потоки одного и того же процесса в разных доменах когерентности. Вместо этого вы запускаете отдельный экземпляр ОС на каждом узле кластера. Я не знаю ни одной реализации C ++, где atomic<T> load / store включают инструкции по ручному сбросу. (Пожалуйста, дайте мне знать, если они есть.)


Заборы / барьеры работают, заставляя текущий поток ждать

... до тех пор, пока необходимая видимость не будет достигнута с помощью обычных механизмов.

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

(К сожалению, Skylake mfence полностью блокирует -of-order выполнение, чтобы исправить неясную ошибку SKL079, связанную с загрузкой NT из памяти WC. Но lock add или xchg или что-то еще, только блокирует последующие загрузки после чтения L1d или буфера хранилища, пока барьер не достигнет конца буфера хранилища . И mfence на более ранних процессорах, по-видимому, также не имеет этой проблемы.)


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

Связанный:


В конечном итоге вопрос, на который я пытаюсь ответить для себя, заключается в том, может ли поток 2 не видеть запись потока 1 в течение нескольких секунд.

Нет, в худшем случае задержка может быть чем-то вроде длины буфера хранилища (56 записей в Skylake, по сравнению с 42 в BDW), умноженное на задержку отсутствия кэша, потому что сильная модель памяти x86 (без переупорядочивания StoreStore) требует, чтобы хранилища фиксировались по порядку. Но RFO для нескольких строк кэша могут быть запущены одновременно, поэтому максимальная задержка может составлять 1/5 от этой (консервативная оценка: имеется 10 буферов заполнения строк). Также может быть конкуренция из-за нагрузок, которые также находятся в полете (или от других ядер), но нам просто нужен порядковый номер обратной стороны конверта.

Допустим, задержка RFO (DRAM или другое ядро) составляет 300 тактовых циклов (в основном составленных) на процессоре с тактовой частотой 3 ГГц. Таким образом, задержка наихудшего случая для того, чтобы хранилище стало глобально видимым, может быть примерно равной 300 * 56 / 5 = 3360 тактов ядра. Итак, с точностью до порядка наихудшего случая будет примерно ~ 1 микросекунда для ЦП с тактовой частотой 3 ГГц, как мы предполагаем. (Частота процессора не учитывается, поэтому оценка задержки RFO в наносекундах была бы более полезной).

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

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

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

person Peter Cordes    schedule 11.07.2018
comment
Я думаю, что мы можем смоделировать время, необходимое хранилищу для достижения другого потока, следующим образом: время, необходимое хранилищу для вывода из эксплуатации (что означает передачу буфера хранилища либо L1D, либо LFB) + время, необходимое для скопируйте строку из частного кэша ядра в частный кеш другого ядра (или в целевой регистр). Это может потребовать передачи L2-L2 для включающего L2 и для разных физических ядер. Но обе эти временные составляющие могут сильно различаться. Очень сложно установить верхний предел для этого. - person Hadi Brais; 12.07.2018
comment
@HadiBrais: вывод на пенсию из вышедшего из строя ядра (ROB) отделен от достижения конца буфера хранения и фиксации в L1d (в строке в состоянии M). Буфер хранилища отделяет выполнение / удаление OoO от фиксации L1d. Выход на пенсию - предварительное условие для принятия решения, но это все. (Большой буфер хранилища может повлиять на задержку прерывания, потому что нет способа отбросить / откатить его; хранилища, которые оставили ROB потребность, чтобы произойти для правильности.) Я исключил OoO exec из моих вычислений, что может иметь значение для определения времени WRT. загружается с низким кодом IPC. - person Peter Cordes; 12.07.2018
comment
Но да, все может сильно отличаться, поэтому я могу с уверенностью исключить микросекунду, но я заявляю только о порядке величины для моей быстрой оценки задержки в наихудшем случае за 1 нас. - person Peter Cordes; 12.07.2018
comment
Рассмотрим систему с общей памятью с несколькими сокетами, где два потока находятся на двух разных сокетах. Сколько времени нужно одному потоку, чтобы получить данные другого? Если все ядра очень заняты чем-то, может ли это занять больше секунды в худшем случае? - person Hadi Brais; 12.07.2018
comment
Возможно, если есть какая-то система приоритезации на межсоединении между сокетами, ядро, ожидающее строки кеша, может голодать. Но я не так много знаю. Здесь? - person Hadi Brais; 12.07.2018
comment
@HadiBrais: хороший вопрос. Внешняя задержка может быть высокой. Или с десятками ядер, борющихся за доступ к одной и той же линии, особенно для операций locked, которые удерживают ее в течение нескольких тактовых циклов, это может быть довольно медленным. Я думал о системах с несколькими сокетами, но, думаю, не учел их в моей оценке наихудшего случая. Может быть, 100 мс - это правдоподобно, может быть, даже 1 мс, если магазин застрял за несколькими другими магазинами, которым всем придется ждать серьезной конкуренции. Мне неизвестна какая-либо система приоритетов в диспетчере конфликтов HW на процессорах Intel или AMD. (И это было отмечено тегом [intel]) - person Peter Cordes; 12.07.2018
comment
Обратите внимание, что OP заинтересован в передаче строки через QPI, как указано в вопросе. - person Hadi Brais; 12.07.2018
comment
@HadiBrais: они говорят, что эти потоки работают на одном узле NUMA, но да, другие конфликты с потоками на других сокетах могут задержать хранилище, которое вам нужно. - person Peter Cordes; 12.07.2018
comment
Да, этот ответ подходит для случая, когда два потока работают на одном узле NUMA. - person Hadi Brais; 12.07.2018
comment
Если вы говорите о наихудших сценариях, я не буду рассчитывать на получение MLP 5, я бы использовал 1. Вы можете не получить MLP магазина, потому что LFB заняты чем-то другим (например, обработкой грузов) или потому что MLP побежден строками, украденными другими ядрами перед фиксацией. Вероятно, есть другие вещи, которые делают его медленнее, чем обычно, например, разделение хранилищ кеша и т. Д. - person BeeOnRope; 12.07.2018
comment
@PeterCordes - Меня интересует эта вещь SKL079. Я думаю, ваша ссылка ведет к комментарию, который был удален. Вы знаете что-нибудь еще? Утверждается, что есть обновление микрокода, которое mfence замедляет работу Skylake, но позволяет избежать переупорядочения с загрузкой NT из памяти WC? В Haswell был HSD162, который заключается в том, что NT, загружаемые из памяти WC, могут передавать заблокированные инструкции, но без исправления, за исключением рекомендации использовать вместо этого mfence. Эта ошибка все еще существует в Skylake. Однако у Haswell нет исправлений для того же сценария. - person BeeOnRope; 12.07.2018
comment
Итак, чисто теоретически, может быть, в Skylake mfence был улучшен, чтобы использовать тот же самый более быстрый механизм, что и lock инструкции (что всегда сбивало с толку: если бы гарантии порядка были одинаковыми, wtf mfence _slower?), Что из-за того, что mfence страдал от тех же ошибок, что и заблокированные инструкции в HSD162, поэтому было создано обновление микрокода, чтобы исправить mfence, который замедлился в Skylake (насколько я не знаю?). Агнер показывает, что у mfence одинаковая задержка и распределение uop на SKL и HSW, хотя ... - person BeeOnRope; 12.07.2018
comment
@BeeOnRope: Я связался со своим ответом на другой вопрос. В последнем разделе этого ответа есть более подробная информация о SKL079 и моих выводах. Если бы кто-нибудь мог протестировать mfence на HSW, чтобы увидеть, блокирует ли он OoO exec, это было бы круто. Хорошая идея сравнить номера HSW; возможно, они пытались сделать mfence более эффективным в SKL, но в итоге вернули его назад. Я предполагал, что ранние уроки были более эффективными, но, возможно, нет. - person Peter Cordes; 12.07.2018