Подразумевает ли модель памяти Java (JSR-133), что вход в монитор сбрасывает кэш данных процессора? - PullRequest
17 голосов
/ 16 июня 2010

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

Для любой архитектуры системы, которая гарантирует когерентность кэша между потоками, проблем нет. Но если архитектура не поддерживает аппаратную когерентность кэша, это, по сути, означает, что всякий раз, когда поток входит в монитор, все изменения памяти, сделанные до этого, должны быть зафиксированы в основной памяти, а кэш должен быть аннулирован. И это должен быть весь кэш данных, а не просто несколько строк, поскольку у монитора нет информации о том, какие переменные в памяти он защищает. Но это, безусловно, повлияет на производительность любого приложения, которое необходимо часто синхронизировать (особенно такие вещи, как очереди заданий с короткими работами). Так может ли Java работать достаточно хорошо на архитектурах без аппаратной когерентности кеша? Если нет, то почему модель памяти не дает более надежных гарантий видимости? Разве не было бы более эффективно, если бы для языка требовалась информация, которую охраняет монитор?

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

Как сейчас, это не имеет особого смысла для меня. Может кто-нибудь выяснить, почему эта конкретная модель памяти была выбрана?


РЕДАКТИРОВАТЬ: мое использование строгого и проигрышного было плохим выбором в ретроспективе. Я использовал «строгий» для случая, когда меньше гарантий и «проиграл» для противоположного. Чтобы избежать путаницы, лучше говорить о более сильных или более слабых гарантиях.

Ответы [ 4 ]

6 голосов
/ 16 июня 2010

абсолютная необходимость синхронизации, даже если когерентность кэша гарантируется аппаратно

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

и, с другой стороны, плохая производительность на несогласованных архитектурах (переполнение полного кэша))

Возможно, я неправильно понял s / t, но с несогласованными архитектурами вам все равно придется синхронизировать критические секции.В противном случае вы столкнетесь с всевозможными условиями гонки из-за переупорядочения.Я не понимаю, почему модель памяти Java усугубляет ситуацию.

не должна ли она быть более строгой (требуется информация, которую охраняет монитор)

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

AFAIK, Java 5 MM (как и .NET CLR MM) более"строгие", чем модели памяти общих архитектур, таких как x86 и IA64.Следовательно, это упрощает рассуждения об этом.Тем не менее, очевидно, что он не должен предлагать s / t, более близкий к последовательной согласованности, потому что это значительно снизит производительность, поскольку может быть применено меньше оптимизаций компилятора / JIT / CPU / кэша.

5 голосов
/ 16 августа 2011

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

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

Наконец, вы, кажется, думаете, что модель памяти Java (JMM) является своеобразной, в то время как ее основы в настоящее время достаточно современны и похожи на блокировки Ada, POSIX (в зависимости от интерпретации стандарта), и модель памяти C / C ++. Возможно, вы захотите прочитать кулинарную книгу JSR-133, в которой объясняется, как JMM реализован в существующих архитектурах: http://g.oswego.edu/dl/jmm/cookbook.html.

4 голосов
/ 15 июня 2011

Ответ будет таким: большинство мультипроцессоров имеют кэш-когерентность , включая большие системы NUMA, что почти? всегда ccNUMA.

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

  • Устройства
  • (память изменена) DMA
  • Кэши данных и кэши инструкций
  • Кэши на других ядрах / процессорах (о которых идет речь в этом вопросе)
  • ...

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

Аналогично, при динамическом генерировании кода вам может потребоваться очистить кэш инструкций.

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

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

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

1 голос
/ 16 июня 2010

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

РЕДАКТИРОВАТЬ: (в общем) кеши памяти не находятся под контролем JVM, JVM не может выбрать чтение / запись / сброс этих кешей, поэтому забудьте о них в этом обсуждении

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

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

Модель памяти Java - это упрощенная модель программирования, которая позволяет средним программистам создавать нормальные многопоточные алгоритмы. под «упрощенным» я подразумеваю, что во всем мире может быть 12 человек, которые действительно прочитали главу 17 JLS и действительно поняли ее.

...