Комментарии 54
Про милионы временных Integer-объектов немного не правда — как минимум от -128 до 127 значения Integer кэшируются.
+5
+ все эти временные Integer будут довольно быстро появляться и умирать в эдене.
У вас есть какие-нить замеры перфоманса на тему Integer vs MutableInteger?
У вас есть какие-нить замеры перфоманса на тему Integer vs MutableInteger?
+3
Я считал 10M строк, которые раскидывались на 4 группы. MutableInteger более чем вдвое быстрее. Собственно, я написал об этом.
0
Накидал грубый тест:
pastebin.com/zKYxyiSG
Результат:
Map<String, MutableInteger> быстрее ~ на 51%
Работа через массив (выгодно если у нас малое/фиксированное колличество ключей) ~ на 48% быстрее.
В тривиальной имплементации (Map<String, Integer>) проседание по перфомансу естественно есть, видимо из-за боксинга, GC работает моментально.
Имплементация c Mutable работает быстрее всего, но совсем немного опережает имплементацию через массив (грубый пример можно посмотреть в тесте)
lany, с вами согласен — в данном конктертом случае mutableInteger более быстр и вполне себе гуд решение, однако с ним надо аккуратно
я бы остановился на решении с массивом, но это уже для каждой задачи своё.
pastebin.com/zKYxyiSG
Результат:
Map<String, MutableInteger> быстрее ~ на 51%
Работа через массив (выгодно если у нас малое/фиксированное колличество ключей) ~ на 48% быстрее.
В тривиальной имплементации (Map<String, Integer>) проседание по перфомансу естественно есть, видимо из-за боксинга, GC работает моментально.
Имплементация c Mutable работает быстрее всего, но совсем немного опережает имплементацию через массив (грубый пример можно посмотреть в тесте)
lany, с вами согласен — в данном конктертом случае mutableInteger более быстр и вполне себе гуд решение, однако с ним надо аккуратно
я бы остановился на решении с массивом, но это уже для каждой задачи своё.
+2
Массив, на мой взгляд, тут как раз загрязняет код сильнее. Ваша функция с массивом и так длиннее вышла, а если предположить, что вам надо заботиться о его растягивании, будет ещё хуже. Но да, зависит от задачи. Я, конечно, не предлагаю панацею, я лишь говорю, что если такой инструмент, который иногда может пригодиться. У нас в проекте на 4000 классов Mutable-числа используются от силы в двух местах, а массивы значительно чаще :-)
0
Вот прямо так и тестировали? Без разогревающих циклов? Без JIT'а? Нда… «Copyright © Luxoft», ничего не скажешь.
+2
Если вы считаете до 10000000, скажем, то как вам этот кэш поможет? Только накладные расходы на проверку, входит ли новое число в этот диапазон.
0
А я еще, по сравнению с первым алгоритмом, мы неплохо выигрываем на том, что избавляемся от
counts.put(next, val+1)
, особенно если у нас частые повторы слов. Давно уже пользуюсь вторым подходом как раз, чтобы избегать лишних вставок в map, на счет боксинга тоже подозревал, но никак руки не доходили проверить0
А еще можно использовать вот это trove.starlight-systems.com/, особенно если работать с такого рода коллекциями нужно много и не смущает дополнительный джарник.
+3
Я бы не советовал так делать, Изменяемые объекты это ужас-ужас.
Кто то возьмет количество из Map, чтобы потом изпользовать, а его под носом изменят. А что с многопоточными приложениями? Не, ну его нафиг такие оптимизации. Кстати — короткоживущие объекты почто не влияют на производительность
Кто то возьмет количество из Map, чтобы потом изпользовать, а его под носом изменят. А что с многопоточными приложениями? Не, ну его нафиг такие оптимизации. Кстати — короткоживущие объекты почто не влияют на производительность
+2
К сожалению, концепция неизменяемых объектов в джаве довольно ущербная — у вас нет стандартных интерфейсов неизменяемых коллекций, у вас нет возможности просто указать _компилятору_, что аргумент/возвращаемое значение — не изменяемое… Нужные интерфейсы можно ввести самому — но это немаленькая работа, и большинство библиотек вас не поддержит. В общем, полноценная реализация разграничения mutable/immutable в яве обойдется крайне дорого. Как правило это разграничение проводится неявно, на основе соглашений. И в этом случае нет никакой разницы, что возвращать изменяемый словарь с неизменяемыми значениями, что возвращать изменяемый словарь с изменяемыми значениями…
+1
В других языках бывает ещё хуже, и ничего — живут и радуются.
0
Моя мысль была такая: от замены Map[String, Integer] на Map[String,MutableInteger] ничего концептуально не ухудшится. Неизменяемости и до того толком не было, поэтому нет особого смысла жаловаться, что она-де пропадет.
А что с этим жить можно — безусловно. И жить, и даже кое-где подправлять.
А что с этим жить можно — безусловно. И жить, и даже кое-где подправлять.
0
На самом деле можно создать интерфейс ImmutableInteger со всеми методами из MutableInteger, кроме set и increment и реализовать его в MutableInteger. и Возвращать соответственно Map[String,ImmutableInteger].
Тогда все станет хорошо.
Тогда все станет хорошо.
0
ну или не плодить свои классы и реализовать в MutableInteger абстрактный Number(как и предлагал автор) и его собственно и возвращать.
0
«На самом деле» нужно делать интерфейс Counters{ int get(K key); Iterable keys(); } — это если вы в самом деле хотите правильно.
Об этом я и говорю: предлагаемое автором решение уже заметно неправославно, от замены Integer на MutableInteger ничего не ухудшится. Для приватной реализации сойдет, для public API по-любому плохо.
Об этом я и говорю: предлагаемое автором решение уже заметно неправославно, от замены Integer на MutableInteger ничего не ухудшится. Для приватной реализации сойдет, для public API по-любому плохо.
+1
Языковая поддержка неизменяемых коллекций, конечно никакая, но это не знит, что надо делать еще хуже. Если я возвращаю не коллекцию, а счетчик, то лучше чтобы он был неизменяемым
0
Да на здоровье же. Я ж написал:
Ну или в крайнем случае после подсчёта скопировать всё в новую Map.Что не так?
0
А то, что проблема не в Map, а в самих неизменяемых числах. Вам нужно будет еще и их клонировать. Т.е. если на основе этого примера сторить реальное приложение, то надо будет добавить столько всего, что все ваши оптимизации сойдут на нет. Дайте компилятору и виртуальной машине оптимизировать, ради бога. Вы не слышали фразу «Преждевременная оптимизация — корень всего зла»?
0
Ну где я написал, что я рекомендую оптимизировать преждевременно? Что ж вы мне приписываете то, чего я не говорил? Надо в начало каждой статьи вставлять слова, что перед оптимизацией сперва используйте профайлер? По-моему, всем давно очевидно.
У вас очень жизненная заметка «Про релевантность опыта», спасибо.
У вас очень жизненная заметка «Про релевантность опыта», спасибо.
0
Надо в начало каждой статьи вставлять слова, что перед оптимизацией сперва используйте профайлер?
Увы, но надо. Как ни печально, но тех, кто понимает, что в статье описывается достаточно экзотическая ситуация* и решения, которые тут применяются, тоже достаточно экзотичны (хотя и, безусловно, интересны) — на Хабре меньшинство. И это меньшинство довольно хорошо совпадает с другим меньшинством — тех, кто в случае просадок производительности сначала исследуют ситуацию (например, с помощью профайлера), а уже потом начинают что-то исправлять.
* — ситуация, когда java используется для data mining-а экзотична по определению, так как она не очень для этого подходит, ни как язык, ни как среда исполнения.
0
Любой примитивный int — изменяемый объект. Вы их вообще не используете? А если используете, то как же многопоточность? Или вы решили, что я предлагаю использовать MutableInteger всегда? Но я же ясно написал:
Внутренние детали вычислительно нетривиальной операции могут обойтись без высокоуровневых концепций, вам так не кажется?
Кроме тех случаев, когда влияют.
Однако в некоторых случаях вам помогут изменяемые (mutable) числа.
Внутренние детали вычислительно нетривиальной операции могут обойтись без высокоуровневых концепций, вам так не кажется?
Кстати — короткоживущие объекты почто не влияют на производительность
Кроме тех случаев, когда влияют.
0
int передается по значению, а объект по ссылке. Если я куда то передал примитивный счетчик, мне уже не надо волноваться, что кто то его изменит.
Расскажите мне в каких случаях короткоживущие объекты влияют на производительность
Расскажите мне в каких случаях короткоживущие объекты влияют на производительность
0
Вы можете вернуть наружу из алгоритма любую структуру данных. Если вы боитесь за изменчивость, скопируйте результат в неизменяемый тип (я это упомянул в статье). В рассмотренном примере это вообще проблем не несёт. И, надо полагать, вы из тех людей, которые обычные Java-массивы не используют вообще? Ведь их там могут изменить.
Короткоживущие объекты влияют на производительность в примере, рассмотренном в статье.
Короткоживущие объекты влияют на производительность в примере, рассмотренном в статье.
+1
Вот пример из жизни — habrahabr.ru/post/147552/ о котором я недавно писал. В этом случае коротко живущие объекты не только влияли на производительность, но и приводили к постоянному срабатыванию сборщика что просто вешало машину. Я уже не говорю про десятки таких мест в высоконагруженных системах.
0
Присоединюсь к критикам. Конкретно задачу из примера намного лучше решить с использованием AtomicInteger и его атомарного инкремента. Мало того что тривиально добавляется многопоточность, так ещё и никаких левых костылей, затрудняющих чтение кода.
К тому же, если пока вы считали распределение строк, ваши изменяемые числа не дай бог успели постареть и попасть в OldGen — прощай всякая производительность.
К тому же, если пока вы считали распределение строк, ваши изменяемые числа не дай бог успели постареть и попасть в OldGen — прощай всякая производительность.
+1
Конкретно задачу из примера намного лучше решить с использованием AtomicInteger и его атомарного инкремента.Вы проверяли, насколько это быстро? Атомарный инкремент ужасно медленный. Просто ужасно. Попробуйте эту задачу распараллелить, вы больше потеряете, чем приобретёте.
И почему вы считаете, что MutableInteger и increment больше затрудняют чтение кода, чем AtomicInteger и incrementAndGet? Код абсолютно одинаковый.
К тому же, если пока вы считали распределение строк, ваши изменяемые числа не дай бог успели постареть и попасть в OldGenНестрашно, если их мало.
0
Руки оторвать:
— Тем кто придумал Java generic не как first class.
— Тем кто использует Integer вообще в коллекциях.
— Тем кто из-за недостатков подхода из второго пункта придумывает гибриды костылей с велосипедами.
Извините, наболело.
— Тем кто придумал Java generic не как first class.
— Тем кто использует Integer вообще в коллекциях.
— Тем кто из-за недостатков подхода из второго пункта придумывает гибриды костылей с велосипедами.
Извините, наболело.
-5
* — Естественно не только Integer, но и Byte, Short, Long, Float, Double.
-1
В чём проблема с использованием Integer в коллекциях?
+1
А уж чем Byte не угодил — вообще неясно. Байты в стандартной реализации закэшированы абсолютно все, новых объектов при боксинге вообще не создаётся. Почитайте исходник java.lang.Byte.
Любопытно, что мнения комментаторов опять же противоположны, о чём я и сказал в начале статьи: от «накладные расходы невелики, молодые объекты удаляются быстро, используйте Integer и не парьтесь» до «Integer использовать ни в коем случае нельзя, это ужасно» :-)
Любопытно, что мнения комментаторов опять же противоположны, о чём я и сказал в начале статьи: от «накладные расходы невелики, молодые объекты удаляются быстро, используйте Integer и не парьтесь» до «Integer использовать ни в коем случае нельзя, это ужасно» :-)
0
Голову надо отрывать тем критикам, кто мечтает оторвать руки инженерам, решавшим (и довольно успешно решившим) проблемы, о которых оный критик даже не думал толком :)
+6
Мне кажется, вы оторваны от реального мира и его задач. В книжках-то конечно всё идеально прекрасно, одновременно и быстро, и абстрактно, и концептуально правильно.
+2
Может, MutableInteger и полезен, но пример не совсем удачный. Для указанной задачи идеально подходит TObjectIntHashMap из библиотеки Trove с его методом
adjustOrPutValue
.0
Вставлю свои 5 копеек. Если забыть о недостатках и сконцентрироваться на достоинствах подхода с классом оберткой, то он вполне пригоден для использования с оговоркой not thread-safe. Надеюсь автор сам написал класс и еще просто не знаком с commons-lang и классами из пакета org.apache.commons.lang.mutable.
По поводу перформанса. Немного быстрее будет если заменить класс-обертку одноэлементным массивов. И то, и другое HotSpot успешно компилирует в быстрый код, но доступ к элементам массива происходит немного быстрее. Ну и для удобства можно написать вспомогательный класс:
По поводу перформанса. Немного быстрее будет если заменить класс-обертку одноэлементным массивов. И то, и другое HotSpot успешно компилирует в быстрый код, но доступ к элементам массива происходит немного быстрее. Ну и для удобства можно написать вспомогательный класс:
class MutableInt {
static Object create() { return new int[1]; }
static int get(Object ref) { return ((int[])ref)[0]; }
static void set(Object ref, int value) { ((int[])ref)[0] = value; }
static int incAndGet(Object ref) { return ++((int[])ref)[0]; }
static int getAndInc(Object ref) { return ((int[])ref)[0]++; }
}
0
Надеюсь автор сам написал класс и еще просто не знаком с commons-lang и классами из пакета org.apache.commons.lang.mutable.Автор знаком и сослался на commons.lang. Но здесь класс написал сам, чтобы понятнее было.
0
Немного быстрее будет если заменить класс-обертку одноэлементным массивов. И то, и другое HotSpot успешно компилирует в быстрый код, но доступ к элементам массива происходит немного быстрее.
Можно с этого места поподробнее? Почему доступ к элементам массива быстрее?
0
Ответил тут: http://habrahabr.ru/post/152005/
Оказывается разницы нет.
Оказывается разницы нет.
0
Думаю, тут тормоза не из-за создания новых объектов, а из-за двукратного лазания в мапу — при гете и при путе. Если бы можно было получить Map.Entry и модифицировать уже его…
+1
Объясните пожалуйста, а почему нельзя использовать < String, int >?
+1
Потому что так устроены генерики в Java: в них не может быть примитивного типа. Сами попробуйте.
0
Потому что generic'и требуют объекта в качестве параметра.
А упирается это в генерацию кода, совместимого с Java 1.4.
Реально в runtime вместо типа T подставляется ближайший тип, прописанный в extends, и используются приведения типов. Они, естественно, будут безопасными, т.к. компилятор уже все проверил.
А упирается это в генерацию кода, совместимого с Java 1.4.
Реально в runtime вместо типа T подставляется ближайший тип, прописанный в extends, и используются приведения типов. Они, естественно, будут безопасными, т.к. компилятор уже все проверил.
0
Кстати, это одна из причин, почему нельзя сделать так:
public class SuperClass<T> {
public void setT(T obj) {
//...
}
}
public class SubClass<Number> extends SuperClass<Number> {
// Здесь будет ошибка компиляции, т.к. метод с такой сигнатуров уже есть
public void setT(Object obj) {
//...
}
public void setT(Number obj) {
//...
}
}
0
IMHO, решать проблему надо в корне.
generic'и нужно заменить на template'ы с поддержкой примитивных типов.
А если следовать парадигме обратной совместимости, то добавить рядом.
Синтаксис различимый вполне можно сделать.
Преобразование между ними — тоже.
Зато приседаний станет гораздо меньше.
generic'и нужно заменить на template'ы с поддержкой примитивных типов.
А если следовать парадигме обратной совместимости, то добавить рядом.
Синтаксис различимый вполне можно сделать.
Преобразование между ними — тоже.
Зато приседаний станет гораздо меньше.
0
Вот, кажется, для оптимизации целых вычислений в JIT именно MutableInteger может быть очень полезен.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Изменяемые числовые объекты