Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Java Новый топик    Ответить
 Generics - для чего нужно стирание типов  [new]
JohnSparrow
Member

Откуда:
Сообщений: 370
Добрый день.

Вопрос скорее теоретический, но все же. В Java generics информация о типах стирается и на этапе исполнения вместо них используется Object. Чем хуже подход C++/C#, когда для каждого варианта использования generic'a на этапе компиляции создается свой конкретный класс?

В первом томе книги Хорстманна на эту тему есть следующее сообщение:
автор
НА ЗАМЕТКУ C++! В этом отношении обобщения в Java заметно отличаются от шаблонов в C++. Для каждого экземпляра шаблона в C++ получается свой тип. Это неприятное явление называется "раздуванием кода шаблона". Этим недостатком Java не страдает.

Т.н. "раздувание кода шаблона" - действительно неприятное явление?

Второй вопрос. Т.к. на этапе исполнения информация о типах стерта, элементы коллекции извлекаются примерно следующим образом:
ConcreteType item = (ConcreteType)list.Get(0);

Насколько значимы затраты на приведение типов при массированных вызовах list.Get() в данном случае?
24 апр 21, 16:10    [22313622]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
забыл ник
Member

Откуда:
Сообщений: 3471
JohnSparrow
Добрый день.

Вопрос скорее теоретический, но все же. В Java generics информация о типах стирается и на этапе исполнения вместо них используется Object. Чем хуже подход C++/C#, когда для каждого варианта использования generic'a на этапе компиляции создается свой конкретный класс?

В первом томе книги Хорстманна на эту тему есть следующее сообщение:
автор
НА ЗАМЕТКУ C++! В этом отношении обобщения в Java заметно отличаются от шаблонов в C++. Для каждого экземпляра шаблона в C++ получается свой тип. Это неприятное явление называется "раздуванием кода шаблона". Этим недостатком Java не страдает.

Т.н. "раздувание кода шаблона" - действительно неприятное явление?

Второй вопрос. Т.к. на этапе исполнения информация о типах стерта, элементы коллекции извлекаются примерно следующим образом:
ConcreteType item = (ConcreteType)list.Get(0);

Насколько значимы затраты на приведение типов при массированных вызовах 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 апр 21, 16:23    [22313629]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
asv79
Member

Откуда: Тверь
Сообщений: 3319
JohnSparrow

Насколько значимы затраты на приведение типов при массированных вызовах list.Get() в данном случае?

Сам как раз на днях задался этим вопросом - так как нужно было сделать оптимизацию
при анализе обнаружил чо касты практически не влияют на производительность
на примере перегона 15 тысяч записей из монго в бд я не обнаружил ощутимой разницы с кастами и без.
Тоесть по факту перегонка шла 11 мин+- какие то секунды ,тоже самое с и кастами теже 11 мин
причем касты вложенные из объекта в лист далее в какой то определенный тип - например тот же Document оттуда еще каст))
так что на счет этого не думай вообще,сколько бы ты там не кастовал на производительность это не повлияет.
24 апр 21, 18:20    [22313681]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
asv79
Member

Откуда: Тверь
Сообщений: 3319
забыл ник

Насколько значимы затраты на приведение типов при массированных вызовах list.Get() в данном случае?


Понамешано конечно у вас в вопросе...
В Scala есть подобная штука, но там ты должен добавить аннотацию specialised и указать прямо типы для которых сгенерить оптимальный код. [/quot]
в java это тоже уже есть(точней скоро будет)-немного не таком виде правда
24 апр 21, 18:22    [22313682]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 8254
asv79
JohnSparrow

Насколько значимы затраты на приведение типов при массированных вызовах list.Get() в данном случае?

Сам как раз на днях задался этим вопросом - так как нужно было сделать оптимизацию
при анализе обнаружил чо касты практически не влияют на производительность
на примере перегона 15 тысяч записей из монго в бд я не обнаружил ощутимой разницы с кастами и без.
Тоесть по факту перегонка шла 11 мин+- какие то секунды ,тоже самое с и кастами теже 11 мин
причем касты вложенные из объекта в лист далее в какой то определенный тип - например тот же Document оттуда еще каст))
так что на счет этого не думай вообще,сколько бы ты там не кастовал на производительность это не повлияет.
это и без анализа понятно что не влияет. Чуйка должна работать.
24 апр 21, 18:31    [22313684]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
asv79
Member

Откуда: Тверь
Сообщений: 3319
PetroNotC Sharp
asv79
пропущено...

Сам как раз на днях задался этим вопросом - так как нужно было сделать оптимизацию
при анализе обнаружил чо касты практически не влияют на производительность
на примере перегона 15 тысяч записей из монго в бд я не обнаружил ощутимой разницы с кастами и без.
Тоесть по факту перегонка шла 11 мин+- какие то секунды ,тоже самое с и кастами теже 11 мин
причем касты вложенные из объекта в лист далее в какой то определенный тип - например тот же Document оттуда еще каст))
так что на счет этого не думай вообще,сколько бы ты там не кастовал на производительность это не повлияет.
это и без анализа понятно что не влияет. Чуйка должна работать.

чуйка работает)
есть цикл - создаем объект в нем и с ним работаем
либо кастуем каждый раз - в итоге на 15 к итераций разница в несколько минут
поэтому лучше на существующем объекте хоть обкастоваться чем новый создавать ,особенно это касается коллекций
24 апр 21, 19:10    [22313694]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
Stanislav Bashkyrtsev
Member

Откуда: СПб
Сообщений: 137
JohnSparrow
В Java generics информация о типах стирается и на этапе исполнения вместо них используется Object. Чем хуже подход C++/C#, когда для каждого варианта использования generic'a на этапе компиляции создается свой конкретный класс?

Я про стирание типов знаю только как о недостатке. Страшно ли что кол-во классов разбухнет от шаблонов? Может в некоторых случаях и так, но в .NET'e решили именно так сделать а не идти по стопам Java (которая как выше было сказано должна была это сделать для обратной совместимости), думаю это все не спроста. Но то что мы не можем создать Generic с примитивом порой сильно бьет по производительности. И приходится либо писать/использовать не стандартные коллекции написанные под каждый тип отдельно.

И да, в C# женерики генерируются в runtime. Но что интересно новые классы создаются только на каждый тип примитива (если он был нужен в коде), а на ссылочные типы создается только одна реализация:
C# guide
Unlike 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 типы стираются у объектов. У классов они остаются. Т.е. если мы напишем
class MyCustomList implements List<User> {}
то инфа про то что там указан User сохраняется и ее можно вытащить через Reflection.
забыл ник
можно либо докупить новый мощный сервер либо распараллелить работу на 2 мелких, это будет дешевле чем платить программисту за оптимизацию
Ну или не будет. Если код написан не оптимально, то он будет медленно работать как при вертикальном, так и при горизонтальном масштабировании. Даже самое крутое железо не сделает из квадратичной сложности логарифмическую. А добавив машинки мы увеличим throughput, но не улучшим latency.
PetroNotC Sharp
это и без анализа понятно что не влияет. Чуйка должна работать.
Чуйка - это наугад. Еще полезно вещи знать точно. Про производительность можно сколько угодно гадать, но там столько нюансов, что проверять нужно обязательно.
24 апр 21, 21:02    [22313724]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
JohnSparrow
Member

Откуда:
Сообщений: 370
забыл ник

2) Почему реализовано стирание типов. Все очень просто - главная фишка Java это обратная совместимость. Generics ввели только в версии 1.5, а до этого с коллекциями работали через Object. Ну собственно чтобы старый код не сломать и придумали это стирание, и вместо типобизеопасного кода на кэране компилятор добавляет рантайм касты.


Это был единственный ответ на мой вопрос, спасибо. Что-то подобное, припоминаю, встречал в книге г-на Хорстманна, так что, вероятно, обратная совместимость и есть причина.

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

Причина вопроса. Изучая Java (в данном случае после С++/С#, но не суть) я пишу некий код, например, с LinkedList<SomeType>. Не зная про стирание типов ожидаю, что он будет откомпилирован именно в LinkedList, оперирующий SomeType или его потомками. Вместо этого получаю LinkedList<Object>, а в качестве бонуса - встроенное приведение типов, да еще и невозможность в рамках одного класса сделать перегрузки вида
    public void SomeFunc(List<String> arg) {
    }

    public void SomeFunc(List<Date> arg) {
    }

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

По всему остальному. На С++ я программировал давно, тогда это дело называлось шаблонами, их не использовал. Потом писал на C#, а там набор готовых коллекций, как и в Java, построен на дженериках (только как в С++, порождающих новые классы для каждого набора типов аргументов). Относительно Вашего вопроса про Reflection применительно к C#: если в одной сборке есть переменная LinkedList<SomeType>, а в подгружаемой сборке есть класс - потомок SomeType или реализующий SomeType (если SomeType - интерфейс), то никаких проблем. Да, в случае с подгружаемой сборкой будет выполняться приведение типов между реальными объектами и SomeType, но на этапе выполнения список будет именно LinkedList<SomeType>, а не LinkedList<Object>.

Затраты времени на приведение типов, конечно, незначительны, да и Java - не Ассемблер. Но это, все таки, минус. Почитал обсуждение, стало интересно, насколько значимый. Понятно, что приведение типов при загрузке данных из БД - не самое узкое место (тест, кстати, не совсем адекватен задаче; а если файлы копировать, в т.ч. гигабайтного объема, приведения типов вообще никак заметно не будет). Сделал условно чистый пример - поиск максимальной строки в массиве большого объема. Один массив - из строк, второй - из объектов (по факту - тех же строк). Во случае с массивом объектов время поиска устойчиво на 5-6% больше, а разница только в наличии/отсутствии приведения типов. Вот код теста:
+

import java.util.UUID;

public class Main {

    public static void main(String[] args) {
        final int count = 1_000_000;

        String strings[] = new String[count];
        Object objects[] = new Object[count];

        long start, durationString, durationObject;

        // init arrays
        String s;
        for(int i = 0; i < count; i++) {
            s = UUID.randomUUID().toString();
            strings[i] = s;
            objects[i] = s;
        }

        // process object array
        String maxObject = "";
        start = System.nanoTime();
        for(int i = 0; i < count; i++) {
            s = (String)objects[i];
            if(s.compareTo(maxObject) > 0)
                maxObject = s;
        }
        durationObject = System.nanoTime() - start;

        // process string array
        String maxString = "";
        start = System.nanoTime();
        for(int i = 0; i < count; i++) {
            s = strings[i];
            if(s.compareTo(maxString) > 0)
                maxString = s;
        }
        durationString = System.nanoTime() - start;
    }
}


На 10 млн. записей время поиск макс. строки в массиве строк - 419 мс, в массиве объектов - 458 мс, разница - 39 мс, т.е. 9.3% от времени на обработку массива строк. Почти 10% - неплохо так. Тест сделал раз 20, результаты все время схожие.

----
Пока писал, не увидел новое сообщение. Да, точно, упаковка/распаковка типов значений для работы с коллекциями - тоже плохо, да и затратно.

Сообщение было отредактировано: 24 апр 21, 21:01
24 апр 21, 21:06    [22313725]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 8254
Stanislav Bashkyrtsev,
Не. Приведение типов работает быстро. Либо это не узкое место.
Я же прикладник. Не будет тут тормозов для прикладной задачи.
Для надуманной типа класс из 600 полей возможно.
24 апр 21, 22:10    [22313749]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
Stanislav Bashkyrtsev
Member

Откуда: СПб
Сообщений: 137
JohnSparrow
На 10 млн. записей время поиск макс. строки в массиве строк - 419 мс, в массиве объектов - 458 мс, разница - 39 мс, т.е. 9.3% от времени на обработку массива строк. Почти 10% - неплохо так. Тест сделал раз 20, результаты все время схожие.
Не, это такой себе бенчмарк, на не разогретой JVM.. Что скажет если поменять местами эти циклы? :) К бенчмаркам нужно подходить с осторожностью, написать их хорошо не так просто, да и понять результаты тоже. А пока не умеешь (а их мало кто умеет писать), то к любым результатам нужно подходить с долей недоверия.

Сообщение было отредактировано: 24 апр 21, 22:32
24 апр 21, 22:40    [22313763]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
JohnSparrow
Member

Откуда:
Сообщений: 370
Stanislav Bashkyrtsev
Не, это такой себе бенчмарк, на не разогретой JVM.. Что скажет если поменять местами эти циклы? :) К бенчмаркам нужно подходить с осторожностью, написать их хорошо не так просто, да и понять результаты тоже. А пока не умеешь (а их мало кто умеет писать), то к любым результатам нужно подходить с долей недоверия.

Циклы местами менял, код есть, можете сами проверить. Результаты (численные значения) получены из-под отладчика IntelliJ.
Дело не в бенчмарках, удивило стирание типов, как таковое. Приведение типов и пр. моменты - частные следствия.

Сообщение было отредактировано: 24 апр 21, 23:17
24 апр 21, 23:24    [22313783]     Ответить | Цитировать Сообщить модератору
 Re: Generics - для чего нужно стирание типов  [new]
Stanislav Bashkyrtsev
Member

Откуда: СПб
Сообщений: 137
JohnSparrow
Stanislav Bashkyrtsev
Не, это такой себе бенчмарк, на не разогретой JVM.. Что скажет если поменять местами эти циклы? :) К бенчмаркам нужно подходить с осторожностью, написать их хорошо не так просто, да и понять результаты тоже. А пока не умеешь (а их мало кто умеет писать), то к любым результатам нужно подходить с долей недоверия.

Циклы местами менял, код есть, можете сами проверить.
Так я проверил, у меня выходит что кто первый, тот и медленней.
25 апр 21, 00:48    [22313803]     Ответить | Цитировать Сообщить модератору
Все форумы / Java Ответить