Я ясен на использовании MemoryBarrier, но не на том, что происходит негласно во времени выполнения. Кто-либо может дать хорошее объяснение того, что продолжается?
В действительно сильной модели памяти испускание инструкций ограждения было бы ненужным. Все обращения к памяти будут выполняться по порядку, и все хранилища будут видны глобально.
Изгороди памяти необходимы, потому что текущие общие архитектуры не обеспечивают сильной модели памяти - x86 / x64 могут, например, переупорядочивать чтение относительно записи. (Более подробный источник - «Руководство разработчика программного обеспечения для архитектур Intel® 64 и IA-32 , 8.2.2 Порядок памяти в процессорах P6 и более поздних семействах» ). Как пример из gazillions, алгоритм Деккера не сработает на x86 / x64 без ограждений.
Даже если JIT создает машинный код, в котором инструкции с загрузкой и сохранением памяти тщательно размещены, его усилия будут бесполезны, если ЦП затем переупорядочит эти загрузки и сохранения - что он может, пока сохраняется иллюзия последовательной согласованности. для текущего контекста / потока.
Рискует чрезмерное упрощение: это может помочь визуализировать загрузки и сохранения, возникающие в результате потока инструкций, как грохочущее стадо диких животных. Когда они пересекают узкий мост (ваш процессор), вы никогда не можете быть уверены в этом порядок животных, так как некоторые из них будут медленнее, некоторые быстрее, некоторые догонят, некоторые отстанут. Если в начале - когда вы испускаете машинный код - вы разбиваете их на группы, помещая бесконечно длинные между ними, вы, по крайней мере, можете быть уверены, что группа A предшествует группе B.
Ограничения обеспечивают упорядочение операций чтения и записи.Формулировка неточная, но:
То, что JIT испускает для полного забора, зависит от архитектуры (ЦП) и того, какой порядок памяти он обеспечивает. Поскольку JIT точно знает, на какой архитектуре он работает, он может выдать правильную инструкцию ( с).
На моей машине x64 с .NET 4.0 RC это блокировка или
.
int a = 0;
00000000 sub rsp,28h
Thread.MemoryBarrier();
00000004 lock or dword ptr [rsp],0
Console.WriteLine(a);
00000009 mov ecx,1
0000000e call FFFFFFFFEFB45AB0
00000013 nop
00000014 add rsp,28h
00000018 ret
Руководство разработчика программного обеспечения для архитектур Intel® 64 и IA-32 Глава 8.1.2:
«... заблокированные операции сериализуют все невыполненные операции загрузки и сохранения (то есть дождаться их завершения)». ... "Заблокированные операции являются атомарными по отношению ко всем остальным операциям с памятью и всем внешне видимым событиям. {{1} } заблокированные инструкции. Заблокированные инструкции могут использоваться для синхронизации данных, записываемых одним процессором и считываемых другим процессором ".
Инструкции упорядочивания памяти удовлетворяют эту конкретную потребность. MFENCE
можно было бы использовать в качестве полного барьера в приведенном выше случае (по крайней мере, теоретически - для одного, заблокированные операции могут быть быстрее , для двух он может привести к другому поведению ). MFENCE
и его друзья могут быть найдены в главе 8.2.5 «Усиление или ослабление модели упорядочивания памяти» .
Есть еще несколько способов сериализации хранилищ и загрузок, хотя они либо непрактичны, либо медленнее, чем описанные выше методы:
В главе 8.3 вы можете найти полные инструкции сериализации , такие как CPUID
. Они также сериализуют последовательность инструкций: «Ничто не может передать инструкцию сериализации, и
инструкция сериализации не может передавать никакую другую инструкцию (чтение, запись, инструкция
выборка или ввод-вывод)» .
Если вы настроили память как сильную некэшированную (UC), , это даст вам сильную модель памяти : никакие спекулятивные или неупорядоченные обращения не будут разрешены, и все обращения будут отображаться на шине, поэтому нет необходимости выдавать инструкцию. :) Конечно, это будет чуть медленнее обычного.
...
Так что это зависит от. Если бы существовал компьютер с надежными гарантиями упорядочивания, JIT, вероятно, ничего не выдал бы.
IA64 и другие архитектуры имеют свои собственные модели памяти - и, следовательно, гарантии упорядочения памяти (или их отсутствия) - и свои собственные инструкции / способы работы с памятью хранения / упорядочением загрузки.
При параллельном программировании без блокировок необходимо заботиться о переназначении инструкций программы.
Переназначение инструкций программы может происходить на нескольких этапах:
Ограждения памяти - единственный способ обеспечить определенный порядок инструкций вашей программы. По сути, ограждение памяти - это класс инструкций, который заставляет центральный процессор применять ограничение упорядочивания. Ограждения памяти можно разделить на три категории:
В .NET Framework существует множество способов создания ограждений: Interlock, Monitor, ReaderWriterLockSlim и т.д.
Thread.MemoryBarrier создает полное ограждение как на уровне JIT-компилятора, так и на уровне процессора.