Как стать автором
Обновить

Комментарии 35

НЛО прилетело и опубликовало эту надпись здесь

Если не поленюсь, будет продолжение.

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

Лямбды быстрее анонимных классов за счет того, что лямбды работают на invokedynamic, а анонимные классы через invokestatic, который несколько медленнее.

Во первых, лямбды не работают на invokedynamic, а создаются через invokedynamic, а объекты анонимных классов через new AClass$1(), то есть просто new и вызов конструктора. Первое создание лямбды много дороже, чем создание экземпляра анонимного класса, потому что нужно еще породить в рантайме анонимный класс завертку для лямбды, загрузить его, а только потом создать экземпляр этого порожденного класса. В случае анонимного класса, нужно только загрузить класс и создать экземпляр (тут кстати надо еще мерить, что быстрее прочитать готовый класс с диска или породить его на лету). Во второй же раз, если у лямбды не было контекста, то лямбда-объект автоматом закэшируется в объекте CallSite инструкции invokedynamic, то есть новых объектов создаваться не будет. В случае анонимного класса, каждый раз будет создаваться новый объект, если вы сами его руками не закэшируете. Если у лямбды есть контекст, то в обоих случаях нужно создавать новый объект, и создавать его через invokedynamic будет несколько дороже, чем в случае анонимного класса (хотя со временем, после оптимизаций, может оказаться один фиг).

Что касается invokestatic vs. invokedynamic, то invokestatic самая быстрая из всех invoke* инструкций байткода, потому что не требуется никого динамического диспатча (target всегда известен) и в отличии от invokespecial, который тоже не требует динамического диспатча, не требуется передавать параметр this. Invokedynamic же в общем случае самая медленная из всех invoke* инструкций байткода, хотя она сильно зависит от методхэндла колсайта, который возвращает бутстрэп метод. Если он вернет константный колсайт invokestatic методхэндла, то после некоторых приплясываний JVM, породится идентичный код тому, как если бы вы сразу позвали метод через invokestatic.
«Твой шворц длиннее моего!..»
))
Мое кунг-фу сильнее твоего!
Спасибо за разбор, было довольно интересно. Правда сам предмет спора несколько сомнителен.

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

Во-вторых все эти байтовые размеры в принципе могут поменяться с выходом новой версии компилятора.

В-третьих если говорить про расход памяти в рантайме, то нужно говорить про размер генерируемого native-кода — ведь совсем не обязательно, что больший по размеру байткод даст больший по по размеру native-код.

Да, это всё важные вопросы, до них просто не успел дойти разговор :-) Если не поленюсь, будет продолжение спора. Ну и смысл не в том, чтобы понять, кто прав, а в том, чтобы поковыряться, как что внутри работает.

НЛО прилетело и опубликовало эту надпись здесь
Доля размера которую вы сэкономите таким образом — самое большее десятые доли процента от общего размера.
НЛО прилетело и опубликовало эту надпись здесь

А смысл в этой статье?


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

Не в любом. В C# лямбда компилируется или в анонимный метод (не просто работает похожим образом, а реально в него компилируется), или в Expression Tree — композит, который в рантайме можно отобразить в SQL / OData filter / что угодно. Так что C# лямбды притащены ради уменьшения дырки между абстракциями repository / query object и обычными коллекциями объектов в памяти.

Будете ли вы использовать синтаксический сахар, зная, что ваша программа от этого станет в 10 раз медленнее?

Скала 2.11 компиляет в анонимные классы, скала 2.12 в лямбды.
На реальных проектах у 2.12 jar'ы получаются в два-четыре раза меньше.
Но нагрузка CPU чуть больше (в пределах +10%).

Кстати, про компиляцию лямбд в скале 2.12 была долгая и весьма любопытная история. Никто не желает перевести на хабр? Вроде не было ещё :-)

Неожиданно. А что там CPU ест?

Спасибо за статью! Обязательно ждем продолжения, в такой же простой и легкой для восприятия форме!
ПС
Извините за бестактный вопрос, но как начинающему java разработчику, после ознакомления с вашим выступлением на java 8 puzzler на jPoint 2016, стало интересно применение стримов, можно будет увидеть статью про них такого же характера? Тем более, что в одной из недавних статей ссылались на тикет с YouTrack об «отказе» использования стримов в коде.

А что стримы? Стримы хорошие, надо их использовать, чем больше, тем лучше :D


Тем более, что в одной из недавних статей ссылались на тикет с YouTrack об «отказе» использования стримов в коде.

Больше верьте слухам!

lany: Видимо подразумевается ваш тикет о преобразовании stream-style кода в обычные циклы. На данный момент есть только преобразование из циклов в стримы.
sah4ez32: Стримы штука хорошая. Главное помнить правило отладки — отладка программы вдвое сложнее чем ее написание. Иногда стоит пожалеть своих коллег, которым придется смотреть код.

Ну если встать на цикл вида for(Person person : people) {...}, IDEA предлагает превратить его в indexed loop. Это ж не означает, что надо отказываться от for-each циклов :-)

Интересно, буду рад продолжению. Было бы неплохо оценить производительность кода со Stream API на анон. классах и лямбдах.
P.S. так как использую Kotlin, который в некоторых случаях использует анонимные классы.
НЛО прилетело и опубликовало эту надпись здесь

Вот из-за таких авторов как lany, которые пишут только на русском, потом выясняется, что средняя компетентность среди русскоязычных Java-разработчиков выше, чем не русскоязычных.

Ну, так пусть переводят себе. Чай не баре :).
Мы, вот, как-то не гнушаемся ни переводами, ни язык учить :)
Спасибо! Полезненько… :)

Вот только не совсем корректно с местом на диске вышло, в общем случае 2 файла по 800 байт займут больше места чем один, но размером в 2000 байт.

Да, согласен. Логичнее было зажать в джарик и посмотреть его размер. Всё равно россыпь класс-файлов обычно никто не использует.

NTFS же вроде умеет мелкие файлы в один кластер пихать? Или я что-то путаю?
Ну я и написал — в общем случае. А NTFS может маленькие файлы в прямо в MFT держать, но, например, у меня это уже не работает для 800 байтового файла.

Да и про джарник — из двух файлов по 800 байт jar получается больший, чем из одного 2000 байтового.
Проверяли или умозрительное заключение? И при каком уровне сжатия?
Какие мы строгие :) Скажем так, опыт и «умозрительное заключение» позволило мне это предположить, а эксперимент подтвердил это.

jar cvf a.jar AbstractManagerImpl.class InitializationException.class
a.jar — 1292
AbstractManagerImpl.class — 652
InitializationException.class — 609

jar cvf b.jar PlanetManager.class
b.jar — 1094
PlanetManager.class — 2652

Классы выбрал случайно, что бы по размеру подходили.
С одной стороны — интересный результат, с другой — это явно не чистый эксперимент — в данном случае класс PlanetManager явно отличается от классов AbstractManagerImpl и InitializationException.

Интересно было бы всё же, по аналогии со статьёй — один класс с лямбдой или два класса (с анонимным) реализующих один и тот же функционал сравнить — это было бы более показательно…
Угу, Но что-то мне подсказывает, что результат будет такой же — jar не использует solid сжатие, а расходы на служебную информацию будут примерно одинаковы.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории