Понятно об использовании MemoryBarrier, но не о том, что происходит за кулисами во время выполнения. Может ли кто-нибудь дать хорошее объяснение тому, что происходит?Может ли кто-нибудь дать простое объяснение того, как «Полные заборы» реализованы в .NET с помощью Threading.MemoryBarrier?
ответ
В действительно сильной модели памяти инструкции по искривлению забора не нужны. Все обращения к памяти будут выполняться по порядку, и все магазины будут отображаться во всем мире.
Запоминания памяти необходимы, потому что текущие общие архитектуры do not provide a strong memory model - x86/x64 может, например, переупорядочить чтение относительно записи. (Более тщательный источник "Intel® 64 and IA-32 Architectures Software Developer’s Manual, 8.2.2 Упорядочение памяти в Р6 и более современных семейств процессоров»). В качестве примера из gazillions, Dekker's algorithm will fail on x86/x64 без заборов.
Даже если JIT производит машинный код, в котором инструкции с нагрузками памяти и магазины тщательно размещены, его усилие бесполезно, если процессор затем сортирует эти нагрузки и магазины - что он может, до тех пор, как иллюзия последовательной последовательности сохраняется для текущей контекста/нити
. Рискуя чрезмерное упрощение: это может помочь визуализировать нагрузки и магазины в результате потока инструкций как громоподобное стадо wil d животных. Когда они пересекают узкий мост (ваш процессор), вы никогда не можете быть уверены в порядках животных, так как некоторые из них будут медленнее, быстрее, некоторые обгоняют, некоторые отстают. Если в начале - когда вы испускаете машинный код - вы разбиваете их на группы, помещая между ними бесконечно длинные ограждения, вы можете по крайней мере быть уверены, что группа A подходит к группе B.
Заборы обеспечивают порядок чтения и пишет.Формулировка не является точной, но:
- Магазин забор «ждет» для всех выдающихся операций хранения (записи) для завершения, но не влияет на нагрузки.
- грузовой забор «ждет» для всех выдающихся операций загрузки (чтения), чтобы закончить, но не влияет на магазины.
- полный забор «ждет» для всех операций по хранению и загрузке. Он имеет эффект, который читает и записывает до того, как забор будет выполнен до записи и загрузки, которые находятся на «другой стороне забора» (приходят позже, чем забор).
Что делает JIT для полного забора, зависит от архитектуры процессора и того, что гарантии памяти гарантируют. Поскольку JIT точно знает, на какой архитектуре он работает, он может выдавать правильные инструкции.
На моей машине x64, с .NET 4.0 RC, это lock or
.
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 and IA-32 Architectures Software Developer’s Manual Глава 8.1.2:
"... заблокированные операции сериализации все невыполненные загрузки и сохранения операций (то есть, ждать их завершения)" ... «заблокированные операции являются атомарными в отношении всех других операций с памятью и все внешне видимые события. Получать только инструкции и таблицы страниц имеет доступ, могут пройти заблокированные инструкции. Заблокированные инструкции могут быть использованы для синхронизации данных, записанных на одной процессор и чтение другим процессором ».
заказ на хранение инструкция адрес эта специфическая потребность.
MFENCE
мог бы использоваться как полный барьер в вышеуказанном случае (по крайней мере теоретически - для одного, locked operations might be faster, для двух это might result in different behavior).MFENCE
и его друзьям можно найти в разделе Глава 8.2.5 «Усиление или ослабление модели порядка памяти».
Есть еще несколько способов сериализации магазинов и нагрузки, хотя они непрактичны или медленнее, чем выше методы:
В разделе 8.3 вы можете найти полный сериализации инструкции как
CPUID
, Эти команды сериализовать также: «Ничто не может передавать инструкцию по сериализации и инструкция сериализации не может передавать какую-либо другую инструкцию (чтение, запись, инструкция выборка или ввод-вывод)».Если вы создали память, как сильная некэшированную (UC), it will give you a strong memory model: нет спекулятивной или вне порядка доступов будут разрешено, и все доступы будут появляться на автобусе, поэтому нет необходимости испускать инструкции. :) Конечно, это будет чуть медленнее, чем обычно.
...
Так это зависит от того.Если бы был компьютер с сильными гарантиями порядка, JIT, вероятно, ничего не испустил бы.
IA64 и другие архитектуры имеют свои собственные модели памяти - и, таким образом, гарантируют порядок памяти (или их отсутствие) - и их собственные инструкции/способы работы с хранением или загрузкой памяти.
Выполняя параллельное программирование с блокировкой, необходимо позаботиться о переупорядочении инструкций программы.
программных команд переупорядочения может происходить в несколько этапов:
- C#/VB.NET/F # оптимизации компилятора
- JIT-компилятор оптимизации
- CPU оптимизации.
Запонки памяти - единственный способ обеспечить конкретный порядок ваших программных инструкций. В принципе, забор памяти - это класс инструкций, который заставляет ЦП обеспечивать принудительное ограничение. заборы памяти можно поставить на три категории:
- нагрузки заборов - обеспечить не нагрузок команды процессора двигаться через заборы
- магазин заборы - убедиться в отсутствии инструкции магазина CPU не двигаться через заборы
- Полных заборы - обеспечить нет команды загрузки или магазин CPU двигаться через заборы
в .NET Framework существует множество способов испускают заборы: интерлок, монитор, ReaderWriterLockSlim и т.д.
Thread.MemoryBarrier испускает полный забор как на компиляторе JIT, так и на уровне процессора.
Ваше последнее предложение - это то, что я ищу. Я знаю, что такое заборы и почему они нужны, но как это испускается на компиляторе JIT и что на самом деле выводится? – Kilhoffer
Отличное объяснение. Хорошие ссылки на другие ресурсы. Спасибо! – Kilhoffer