Проблема не только в конкретных инструкциях. Есть еще skid, который происходит из-за того, что прерывание срабатывает не мгновенно, зависимости по данным, от которых зависит глубина конвейеризации и влияние того, где находятся эти данные (L1-кэш, L2, ...).
Очень много микро-архитектурных эффектов, которые, как мне кажется, почти невозможно предусмотреть и которые меняются от процессора к процессору. Поэтому это скорее непредсказуемый фактор, который очень тяжело учесть в профайлере
Это вы фантазируете или имеются в виду какие-то конкретные результаты?
Автор, конечно, вряд ли имел это в виду, но в компиляторах полно NP-полных задач: аллокация регистров, instruction scheduling, минимизация количества барьеров памяти.
Речи о "нельзя оптимизировать код лучше", конечно, не идет, но практический (да пусть даже и экспоненциальный) алгоритм может дать мощный толчок компиляторным оптимизациям.
В реализации это учитывается при помощи allocation fraction: если поток был создан между сборками и не успел много нааллоцировать, то он получит маленький TLAB как раз из-за того что его allocation fraction будет небольшим.
Не пропущен, там просто опущены неинтересные детали про выбор "идти в быструю ветку или в медленную" и получился такой некрасивый переход, постараюсь как-нибудь поправить.
Автоматический префетч срабатывает только когда идут последовательные обращения к памяти с фиксированной дельтой, а тут паттерн "аллокация -> произвольные действия -> следующая аллокация" и нужен префетч памяти под следующую аллокацию. Аналогичным образом можно было бы префетчить, например, итерацию по linked-list'у
Не стоит делать такие утверждения не будучи членом команды, которая принимала design decision о необходимости inline.
1) Inline-лямбды невероятно рулят на андроиде, где без inline приходится под каждую лямбду генерировать анонимный класс, раздувая итоговый размер приложения.
2) Без inline-лямбд гораздо сложнее сделать (и возможно только более ограниченную версию) reified generics и non-local returns.
3) Конструкции с лямбдами в котлине активно используются в качестве очень приятного сахара (классический пример c lock из документации), логично было бы сделать, чтоб такой сахар компилировался в тот же байткод, что аналогичная (и более громоздкая в случае с try-finally) конструкция на джаве.
4) JIT вполне себе сносно инлайнит лямбды. Да, на эту оптимизацию нельзя полагаться; тем не менее полагаться на то, что средний разработчик умеет инлайнить лучше, чем компилятор уж тем более не стоит. Вот бенчмарк, где JIT инлайнит и метод, и переданную в него лямбду.
5) Было бы странно, если бы решения о дизайне языке принимались только на основании того, что неспецифицированный JIT-компилятор может или не может сделать.
Статья выглядит очень подозрительно с точки зрения осведомленности автора:
Диаграмма состояний и доказательство невозможности выглядит странно, хотя в том же комментарии в oopMark.hpp видно, что в заголовке просто не хватает места на хэшкод и указатель на поток + эпоху.
Когда за блокировку конкурируют два потока, привязанная блокировка отключается в любом случае, так что между двумя методам хеширования не наблюдается значимой разницы.
Это может и правда, но у автора в бенчмарке ошибка копипасты и в обоих случаях он синхронизируется на withoutIdHash
В версиях 6 и 7 это случайно генерируемое число. В версиях 8 и 9 это число, полученное на основании состояния потока
И в переводе, и в оригинале это звучит так, будто в 8 и 9 число уже неслучайное.
Оно все еще случайно, просто seed теперь thread-local (и не один), а сам генератор работает по-другому.
А на
Integer.valueOf(i)
заметно хуже работает escape analysis в нетривиальных случаях, с комментария про это и началась эта ветка обсуждения :)К сожалению, обертка не всегда подходит и иногда нужен именно тип
Integer
, поэтому приходится использоватьnew Integer(i)
.Интересно, получится ли в итоге такой конструктор удалить в будущих версиях Java, не сломав существенную часть экосистемы
Проблема не только в конкретных инструкциях. Есть еще skid, который происходит из-за того, что прерывание срабатывает не мгновенно, зависимости по данным, от которых зависит глубина конвейеризации и влияние того, где находятся эти данные (L1-кэш, L2, ...).
Очень много микро-архитектурных эффектов, которые, как мне кажется, почти невозможно предусмотреть и которые меняются от процессора к процессору. Поэтому это скорее непредсказуемый фактор, который очень тяжело учесть в профайлере
Автор, конечно, вряд ли имел это в виду, но в компиляторах полно NP-полных задач: аллокация регистров, instruction scheduling, минимизация количества барьеров памяти.
Речи о "нельзя оптимизировать код лучше", конечно, не идет, но практический (да пусть даже и экспоненциальный) алгоритм может дать мощный толчок компиляторным оптимизациям.
Скорее обчитался gvsmirnov :)
Плохо сформулировал.
В реализации это учитывается при помощи allocation fraction: если поток был создан между сборками и не успел много нааллоцировать, то он получит маленький TLAB как раз из-за того что его allocation fraction будет небольшим.
Про равномерность ответил в соседнем треде.
Не пропущен, там просто опущены неинтересные детали про выбор "идти в быструю ветку или в медленную" и получился такой некрасивый переход, постараюсь как-нибудь поправить.
В изначальной гипотезе это никак не учитывается, это самое простое приближение, которым можно воспользоваться: все TLAB заполняются равномерно.
В реализации это используется с помощью учитывания allocation fraction.
Не стоит делать такие утверждения не будучи членом команды, которая принимала design decision о необходимости inline.
1) Inline-лямбды невероятно рулят на андроиде, где без inline приходится под каждую лямбду генерировать анонимный класс, раздувая итоговый размер приложения.
2) Без inline-лямбд гораздо сложнее сделать (и возможно только более ограниченную версию) reified generics и non-local returns.
3) Конструкции с лямбдами в котлине активно используются в качестве очень приятного сахара (классический пример c lock из документации), логично было бы сделать, чтоб такой сахар компилировался в тот же байткод, что аналогичная (и более громоздкая в случае с try-finally) конструкция на джаве.
4) JIT вполне себе сносно инлайнит лямбды. Да, на эту оптимизацию нельзя полагаться; тем не менее полагаться на то, что средний разработчик умеет инлайнить лучше, чем компилятор уж тем более не стоит.
Вот бенчмарк, где JIT инлайнит и метод, и переданную в него лямбду.
5) Было бы странно, если бы решения о дизайне языке принимались только на основании того, что неспецифицированный JIT-компилятор может или не может сделать.
NB: я к дизайну языка никакого отношения не имею
Статья выглядит очень подозрительно с точки зрения осведомленности автора:
Диаграмма состояний и доказательство невозможности выглядит странно, хотя в том же комментарии в oopMark.hpp видно, что в заголовке просто не хватает места на хэшкод и указатель на поток + эпоху.
Это может и правда, но у автора в бенчмарке ошибка копипасты и в обоих случаях он синхронизируется на withoutIdHash
И в переводе, и в оригинале это звучит так, будто в 8 и 9 число уже неслучайное.
Оно все еще случайно, просто seed теперь thread-local (и не один), а сам генератор работает по-другому.
Ну и флаг для смены генератора конечно же есть.
В девятке должны поправить проблему со String Deduplication:
https://bugs.openjdk.java.net/browse/JDK-8158871