openjdk.ruOpenJDK на русском

JEP 142: Reduce Cache Contention on Specified Fields

Снижение конкуренции за кэш на указанных полях

AuthorsJesper Wilhelmsson, Tony Printezis
ОтветственныйTony Printezis
ТипFeature
ОбластьImplementation
СтатусClosed / Delivered
Выпуск8
Компонентhotspot / gc
Обсуждениеhostspot dash dev at openjdk dot java dot net
ТрудоёмкостьM
РецензентыDoug Lea
ОдобренMikael Vidstedt
Создан2011/09/22 20:00
Обновлён2017/06/15 18:05
Задача8046132

Аннотация

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

Описание

Конкуренция за память возникает, когда две ячейки памяти, с которыми работают два разных ядра, оказываются в одной строке кэша и хотя бы одно из ядер выполняет запись. Для ячеек памяти с высокой конкуренцией это может стать серьёзной проблемой производительности и масштабируемости. Цель этого улучшения — избежать конкуренции за память между ядрами, по крайней мере для полей, которые мы можем легко определить во время разработки.

Идея состоит в том, чтобы добавлять выравнивающие заполнители до и после каждого поля, за которое может возникать конкуренция, чтобы никакое другое поле (или другой объект) не могло оказаться в той же строке кэша. В общем случае, когда выравнивание объектов не гарантируется, размер заполнителя должен совпадать с размером строки кэша машины, на которой мы работаем. Если можно гарантировать определённое выравнивание объектов, объём необходимого заполнения можно уменьшить. Например, если гарантируется, что первое поле объекта всегда находится в начале строки кэша, то перед полем достаточно добавить ровно столько заполнения, чтобы поле тоже оказалось в начале строки кэша, а после него — столько, чтобы следующее поле оказалось в начале следующей строки кэша.

Такое заполнение можно достаточно просто реализовать во время загрузки класса, добавив в класс нужное количество фиктивных полей. Изменить компоновку класса позже было бы гораздо сложнее, особенно после того, как экземпляры этого класса уже были выделены и/или некоторые его методы уже были скомпилированы JIT-компилятором.

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

Главная сложность — как дать разработчикам возможность указать, за какие поля может возникать конкуренция. Один из универсальных способов — использовать аннотации (хотя для этого нужен доступ к исходному коду). Так JVM сможет обработать указанные поля наилучшим образом (т. е. либо просто добавив заполнение, либо сочетая заполнение и выровненное выделение памяти, как описано выше).

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

Влияние

  • Производительность/масштабируемость: цель — повысить производительность многопоточных приложений и улучшить их масштабируемость. Заполнение объектов влечёт за собой повышенное потребление памяти.