|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
Добрый день. Вопрос скорее теоретический, но все же. В Java generics информация о типах стирается и на этапе исполнения вместо них используется Object. Чем хуже подход C++/C#, когда для каждого варианта использования generic'a на этапе компиляции создается свой конкретный класс? В первом томе книги Хорстманна на эту тему есть следующее сообщение: авторНА ЗАМЕТКУ C++! В этом отношении обобщения в Java заметно отличаются от шаблонов в C++. Для каждого экземпляра шаблона в C++ получается свой тип. Это неприятное явление называется "раздуванием кода шаблона". Этим недостатком Java не страдает. Т.н. "раздувание кода шаблона" - действительно неприятное явление? Второй вопрос. Т.к. на этапе исполнения информация о типах стерта, элементы коллекции извлекаются примерно следующим образом: Код: java 1.
Насколько значимы затраты на приведение типов при массированных вызовах list.Get() в данном случае? ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 16:10 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
JohnSparrow Добрый день. Вопрос скорее теоретический, но все же. В Java generics информация о типах стирается и на этапе исполнения вместо них используется Object. Чем хуже подход C++/C#, когда для каждого варианта использования generic'a на этапе компиляции создается свой конкретный класс? В первом томе книги Хорстманна на эту тему есть следующее сообщение: авторНА ЗАМЕТКУ C++! В этом отношении обобщения в Java заметно отличаются от шаблонов в C++. Для каждого экземпляра шаблона в C++ получается свой тип. Это неприятное явление называется "раздуванием кода шаблона". Этим недостатком Java не страдает. Т.н. "раздувание кода шаблона" - действительно неприятное явление? Второй вопрос. Т.к. на этапе исполнения информация о типах стерта, элементы коллекции извлекаются примерно следующим образом: Код: java 1.
Насколько значимы затраты на приведение типов при массированных вызовах list.Get() в данном случае? Понамешано конечно у вас в вопросе... 1) Начнем с того почему в java не создаются специализированные версии templates и является ли раздувание кода проблемой. Честно скажу, я C++ знаю поверхностно, поэтому у меня возникает вопрос, а как там обстоят дела если нужно создать template для неизвестного на этапе компиляции класса?(допустим подгрузили модуль с реализацией)? В Java есть механизм reflection, по динамической подгрузке классов, поэтому слабо себе представляю как и главное зачем реализовывать в java специализацию. В Scala есть подобная штука, но там ты должен добавить аннотацию specialised и указать прямо типы для которых сгенерить оптимальный код. Но как показывает практика никому это нафиг не нужно. Далее, так как JVM оптимизирует в рантайме, то ничего в принципе не мешает ей скопилировать версию кода под определенный тип налету(не удивлюсь если так оно и работает). По поводу раздутости - ну бинарник разбухает в случае C++ - насколько критично не мне судить, если это какие микроконтроллеры то вполне вероятно может быть критично. 2) Почему реализовано стирание типов. Все очень просто - главная фишка Java это обратная совместимость. Generics ввели только в версии 1.5, а до этого с коллекциями работали через Object. Ну собственно чтобы старый код не сломать и придумали это стирание, и вместо типобизеопасного кода на кэране компилятор добавляет рантайм касты. Сильно на производительность это не влияет, ты 100% не ощутишь. И вообще думать о таких микрооптимизациях программируя на Java - верная дорога выстрелить себе в голову, JVM в любом случае умнее тебя и соптимизирует лучше, поэтому расслабься и пиши ПОНЯТНЫЙ код, а если вдруг где будет тормозить - разбираться с этим локально. Уже давно никого не интересует в мире Java энтерпрайза последний выжатый из CPU тик, можно либо докупить новый мощный сервер либо распараллелить работу на 2 мелких, это будет дешевле чем платить программисту за оптимизацию ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 16:23 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
JohnSparrow Насколько значимы затраты на приведение типов при массированных вызовах list.Get() в данном случае? Сам как раз на днях задался этим вопросом - так как нужно было сделать оптимизацию при анализе обнаружил чо касты практически не влияют на производительность на примере перегона 15 тысяч записей из монго в бд я не обнаружил ощутимой разницы с кастами и без. Тоесть по факту перегонка шла 11 мин+- какие то секунды ,тоже самое с и кастами теже 11 мин причем касты вложенные из объекта в лист далее в какой то определенный тип - например тот же Document оттуда еще каст)) так что на счет этого не думай вообще,сколько бы ты там не кастовал на производительность это не повлияет. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 18:20 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
забыл ник Насколько значимы затраты на приведение типов при массированных вызовах list.Get() в данном случае? Понамешано конечно у вас в вопросе... В Scala есть подобная штука, но там ты должен добавить аннотацию specialised и указать прямо типы для которых сгенерить оптимальный код. [/quot] в java это тоже уже есть(точней скоро будет)-немного не таком виде правда ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 18:22 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
asv79 JohnSparrow Насколько значимы затраты на приведение типов при массированных вызовах list.Get() в данном случае? Сам как раз на днях задался этим вопросом - так как нужно было сделать оптимизацию при анализе обнаружил чо касты практически не влияют на производительность на примере перегона 15 тысяч записей из монго в бд я не обнаружил ощутимой разницы с кастами и без. Тоесть по факту перегонка шла 11 мин+- какие то секунды ,тоже самое с и кастами теже 11 мин причем касты вложенные из объекта в лист далее в какой то определенный тип - например тот же Document оттуда еще каст)) так что на счет этого не думай вообще,сколько бы ты там не кастовал на производительность это не повлияет. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 18:31 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
PetroNotC Sharp asv79 пропущено... Сам как раз на днях задался этим вопросом - так как нужно было сделать оптимизацию при анализе обнаружил чо касты практически не влияют на производительность на примере перегона 15 тысяч записей из монго в бд я не обнаружил ощутимой разницы с кастами и без. Тоесть по факту перегонка шла 11 мин+- какие то секунды ,тоже самое с и кастами теже 11 мин причем касты вложенные из объекта в лист далее в какой то определенный тип - например тот же Document оттуда еще каст)) так что на счет этого не думай вообще,сколько бы ты там не кастовал на производительность это не повлияет. чуйка работает) есть цикл - создаем объект в нем и с ним работаем либо кастуем каждый раз - в итоге на 15 к итераций разница в несколько минут поэтому лучше на существующем объекте хоть обкастоваться чем новый создавать ,особенно это касается коллекций ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 19:10 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
JohnSparrowВ Java generics информация о типах стирается и на этапе исполнения вместо них используется Object. Чем хуже подход C++/C#, когда для каждого варианта использования generic'a на этапе компиляции создается свой конкретный класс? Я про стирание типов знаю только как о недостатке. Страшно ли что кол-во классов разбухнет от шаблонов? Может в некоторых случаях и так, но в .NET'e решили именно так сделать а не идти по стопам Java (которая как выше было сказано должна была это сделать для обратной совместимости), думаю это все не спроста. Но то что мы не можем создать Generic с примитивом порой сильно бьет по производительности. И приходится либо писать/использовать не стандартные коллекции написанные под каждый тип отдельно. И да, в C# женерики генерируются в runtime . Но что интересно новые классы создаются только на каждый тип примитива (если он был нужен в коде), а на ссылочные типы создается только одна реализация: C# guideUnlike with value types, another specialized version of the Stack<T> class is not created for the Order type. Instead, an instance of the specialized version of the Stack<T> class is created and the orders variable is set to reference it. Так что нет ни большого раздутия, ни стирания типов. JohnSparrowНасколько значимы затраты на приведение типов при массированных вызовах list.Get() в данном случае?Я в JVM реализацию не смотрел, но думаю что это намного менее затратно нежели создание wrapper'a типа Integer/Long. Ну и кстати.. generic типы стираются у объектов. У классов они остаются. Т.е. если мы напишем Код: java 1.
то инфа про то что там указан User сохраняется и ее можно вытащить через Reflection. забыл никможно либо докупить новый мощный сервер либо распараллелить работу на 2 мелких, это будет дешевле чем платить программисту за оптимизациюНу или не будет. Если код написан не оптимально, то он будет медленно работать как при вертикальном, так и при горизонтальном масштабировании. Даже самое крутое железо не сделает из квадратичной сложности логарифмическую. А добавив машинки мы увеличим throughput, но не улучшим latency. PetroNotC Sharpэто и без анализа понятно что не влияет. Чуйка должна работать.Чуйка - это наугад. Еще полезно вещи знать точно. Про производительность можно сколько угодно гадать, но там столько нюансов, что проверять нужно обязательно. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 21:02 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
забыл ник 2) Почему реализовано стирание типов. Все очень просто - главная фишка Java это обратная совместимость. Generics ввели только в версии 1.5, а до этого с коллекциями работали через Object. Ну собственно чтобы старый код не сломать и придумали это стирание, и вместо типобизеопасного кода на кэране компилятор добавляет рантайм касты. Это был единственный ответ на мой вопрос, спасибо. Что-то подобное, припоминаю, встречал в книге г-на Хорстманна, так что, вероятно, обратная совместимость и есть причина. Странно (с точки зрения новичка), что было принято решение обеспечивать обратную совместимость таким способом. В .NET, к примеру, при компиляции для сборки задается целевая платформа (версия .NET). Хотите использовать разные новые возможности - компилируете для соотв. версии .NET, но тогда пользователям придется ее ставить. Не хотите - пишете на старой версии (без дженериков, например), но тогда пользователям меньше проблем. Причина вопроса. Изучая Java (в данном случае после С++/С#, но не суть) я пишу некий код, например, с LinkedList<SomeType>. Не зная про стирание типов ожидаю, что он будет откомпилирован именно в LinkedList, оперирующий SomeType или его потомками. Вместо этого получаю LinkedList<Object>, а в качестве бонуса - встроенное приведение типов, да еще и невозможность в рамках одного класса сделать перегрузки вида Код: java 1. 2. 3. 4. 5.
С точки зрения новичка это странно - компилятор переделывает написанный тобой код, меняя типы данных. Ну а невозможность сделать приведенные выше перегрузки неочевидна (по части синтаксиса и просто нормальной логики нарушений никаких нет). Стирание типов выглядит явным костылем, поэтому заинтересовала причина такого решения. По всему остальному. На С++ я программировал давно, тогда это дело называлось шаблонами, их не использовал. Потом писал на C#, а там набор готовых коллекций, как и в Java, построен на дженериках (только как в С++, порождающих новые классы для каждого набора типов аргументов). Относительно Вашего вопроса про Reflection применительно к C#: если в одной сборке есть переменная LinkedList<SomeType>, а в подгружаемой сборке есть класс - потомок SomeType или реализующий SomeType (если SomeType - интерфейс), то никаких проблем. Да, в случае с подгружаемой сборкой будет выполняться приведение типов между реальными объектами и SomeType, но на этапе выполнения список будет именно LinkedList<SomeType>, а не LinkedList<Object>. Затраты времени на приведение типов, конечно, незначительны, да и Java - не Ассемблер. Но это, все таки, минус. Почитал обсуждение, стало интересно, насколько значимый. Понятно, что приведение типов при загрузке данных из БД - не самое узкое место (тест, кстати, не совсем адекватен задаче; а если файлы копировать, в т.ч. гигабайтного объема, приведения типов вообще никак заметно не будет). Сделал условно чистый пример - поиск максимальной строки в массиве большого объема. Один массив - из строк, второй - из объектов (по факту - тех же строк). Во случае с массивом объектов время поиска устойчиво на 5-6% больше, а разница только в наличии/отсутствии приведения типов. Вот код теста: Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41.
На 10 млн. записей время поиск макс. строки в массиве строк - 419 мс, в массиве объектов - 458 мс, разница - 39 мс, т.е. 9.3% от времени на обработку массива строк. Почти 10% - неплохо так. Тест сделал раз 20, результаты все время схожие. ---- Пока писал, не увидел новое сообщение. Да, точно, упаковка/распаковка типов значений для работы с коллекциями - тоже плохо, да и затратно. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 21:06 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev, Не. Приведение типов работает быстро. Либо это не узкое место. Я же прикладник. Не будет тут тормозов для прикладной задачи. Для надуманной типа класс из 600 полей возможно. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 22:10 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
JohnSparrowНа 10 млн. записей время поиск макс. строки в массиве строк - 419 мс, в массиве объектов - 458 мс, разница - 39 мс, т.е. 9.3% от времени на обработку массива строк. Почти 10% - неплохо так. Тест сделал раз 20, результаты все время схожие. Не, это такой себе бенчмарк, на не разогретой JVM.. Что скажет если поменять местами эти циклы? :) К бенчмаркам нужно подходить с осторожностью, написать их хорошо не так просто, да и понять результаты тоже. А пока не умеешь (а их мало кто умеет писать), то к любым результатам нужно подходить с долей недоверия. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 22:40 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev Не, это такой себе бенчмарк, на не разогретой JVM.. Что скажет если поменять местами эти циклы? :) К бенчмаркам нужно подходить с осторожностью, написать их хорошо не так просто, да и понять результаты тоже. А пока не умеешь (а их мало кто умеет писать), то к любым результатам нужно подходить с долей недоверия. Циклы местами менял, код есть, можете сами проверить. Результаты (численные значения) получены из-под отладчика IntelliJ. Дело не в бенчмарках, удивило стирание типов, как таковое. Приведение типов и пр. моменты - частные следствия. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2021, 23:24 |
|
Generics - для чего нужно стирание типов
|
|||
---|---|---|---|
#18+
JohnSparrow Stanislav Bashkyrtsev Не, это такой себе бенчмарк, на не разогретой JVM.. Что скажет если поменять местами эти циклы? :) К бенчмаркам нужно подходить с осторожностью, написать их хорошо не так просто, да и понять результаты тоже. А пока не умеешь (а их мало кто умеет писать), то к любым результатам нужно подходить с долей недоверия. Циклы местами менял, код есть, можете сами проверить. ... |
|||
:
Нравится:
Не нравится:
|
|||
25.04.2021, 00:48 |
|
|
start [/forum/topic.php?fid=59&msg=40065476&tid=2120467]: |
0ms |
get settings: |
23ms |
get forum list: |
16ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
65ms |
get topic data: |
16ms |
get forum data: |
3ms |
get page messages: |
328ms |
get tp. blocked users: |
2ms |
others: | 15ms |
total: | 476ms |
0 / 0 |