Модель памяти Java: компилятор переставляет строки кода - PullRequest
7 голосов
/ 05 января 2012

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

Мой вопрос (ы):

  1. Что достигается путем предоставления этой свободы компилятору? Действительно ли возможно, чтобы компилятор создавал код, который более эффективен при перестройке кода? Мне еще предстоит увидеть практическое обоснование для этого. Иногда я чувствую, что выгоды, если таковые имеются, значительно перевешиваются рисками параллелизма, которые это может принести.

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

Ответы [ 3 ]

8 голосов
/ 05 января 2012

Компилятор javac работает почти без оптимизаций.

Собственный JIT-компилятор может переупорядочивать инструкции в случае проблем с упорядочением памяти. Однако ЦП также может переупорядочивать инструкции и обновления памяти, которые имеют тот же эффект.

Что достигается путем предоставления этой свободы компилятору?

Основным преимуществом является переносимость кода. Чем больше гарантий вы предоставите, тем труднее будет убедиться, что каждая платформа действительно делает это.

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

Действительно ли возможно, чтобы компилятор генерировал код, который более эффективен при перестройке кода?

Да. но переупорядочение, выполняемое ЦП, является более значительным.

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

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

Именно поэтому вы используете барьеры памяти, такие как volatile, synchronized блоки и Lock. При их использовании вы получаете гарантии безопасности нитей.

Я знаю, что использование примитивов синхронизации эффективно справляется с побочными эффектами перестановки, но я спрашиваю, есть ли прямой способ (опция компилятора), чтобы отключить это?

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

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

2 голосов
/ 05 января 2012

Действительно ли возможно, чтобы компилятор генерировал код, который более эффективен при перестройке кода?

О, да, это так!

Одним из фактов жизни в современной компьютерной архитектуре является то, что память является основным узким местом производительности. Процессор может выполнять регистрацию для регистрации команд во много раз быстрее, чем он может читать и записывать в основную память. Вот почему высокопроизводительные чипы имеют 2 или 3 уровня кэш-памяти. Кроме того, типичные процессоры используют конвейерную обработку, позволяющую одновременно выполнять несколько команд.

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

Примечание: здесь речь идет о значительном ускорении: возможно, в 3-5 раз.

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

2 голосов
/ 05 января 2012

Процесс реорганизации байт-кода управляется JIT (компилятором Just in Time) Вы можете остановить его запуск с помощью следующей опции:

-Djava.compiler=NONE

Подробнее здесь: http://www.cs.swarthmore.edu/~newhall/unixhelp/debuggingtips_Java.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...