Как заставить ядро ​​процессора очистить буфер хранилища в c? - PullRequest
0 голосов
/ 07 января 2019

У меня есть приложение, которое имеет 2 потока, привязку потока A к ядру 1 и привязку потока B к ядру 2, Ядро 1 и ядро ​​2 находятся в одном и том же сокете x86.

поток A выполняет вращение с целым числом x, поток B будет увеличивать x при некоторых условиях. Когда поток B решит увеличить x, он лишит законной силы строку кэша, где расположен x, и в соответствии с протоколом x86 MESI он сохранит новый x для сохранения буфера до того, как core2 получит недействительный ack, затем после того, как core2 получит недействительный ack, core2 очистит буфер хранения.

Мне интересно, core2 сбрасывает буфер хранилища сразу после того, как core2 получает недействительный ack ?! Есть ли шанс, что я могу заставить процессор делать буфер хранилища на языке c? потому что поток A в core1 spining x должен получить x новое значение как можно раньше в моем случае.

Ответы [ 2 ]

0 голосов
/ 07 января 2019

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

Вы можете использовать барьер (например, atomic_thread_fence(memory_order_seq_cst), чтобы создать поток ждать , пока его хранилища не станут глобально видимыми, прежде чем делать какие-либо дополнительные загрузки или накопления, но это работает, блокируя это ядро, а не ускорение очистки буфера хранилища.

Очевидно, чтобы избежать неопределенного поведения в C11, переменная должна быть _Atomic. Если есть только один писатель, вы можете использовать tmp = atomic_load_explicit(&x, memory_order_relaxed) и store_explicit из tmp+1, чтобы избежать более дорогого хранилища seq_cst или атомарного RMW. Порядок acq / rel тоже сработает, просто избегайте seq_cst по умолчанию и избегайте atomic_fetch_add RMW, если есть только один писатель.

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


Прежде чем другое ядро ​​сможет прочитать данные, которые вы написали, оно должно пройти путь от состояния Modified в L1d ядра, которое записало его в кэш-память L3, и оттуда в L1d ядра считывателя.

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

В Ice Lake будет clwb, что оставляет данные в кэше, а также принудительную обратную запись в DRAM. Но опять же, это заставляет данные фактически переходить на DRAM.

Tremont (преемник Goldmont Plus , серия Atom / Silvermont) имеет _mm_cldemote (cldemote). Это как противоположность предварительной выборке SW; это необязательный совет по производительности для записи строки кэша в L3, но не заставляет переходить на DRAM или что-либо еще.

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

0 голосов
/ 07 января 2019

Вам нужно использовать атомику.

Вы можете использовать atomic_thread_fence, если действительно хотите (вопрос немного о проблеме XY), но, вероятно, было бы лучше сделать x атомарной и использовать atomic_store и atomic_load, или, может быть, что-то вроде atomic_compare_exchange_weak.

...