Я работаю над приложением, и один подход к проектированию предполагает чрезвычайно интенсивное использование instanceof
оператора. Хотя я знаю, что дизайн ОО, как правило, старается избегать использования instanceof
, это другая история, и этот вопрос связан исключительно с производительностью. Мне было интересно, есть ли влияние на производительность? Это так же быстро, как ==
?
Например, у меня есть базовый класс с 10 подклассами. В одной функции, которая принимает базовый класс, я проверяю, является ли класс экземпляром подкласса, и выполняю некоторую процедуру.
Один из других способов решения этой проблемы - использовать вместо этого целочисленный примитив «id типа» и использовать битовую маску для представления категорий подклассов, а затем просто сравнить битовую маску подклассов «id типа» с постоянная маска, представляющая категорию.
Как- instanceof
то оптимизируется JVM, чтобы быть быстрее, чем это? Я хочу придерживаться Java, но производительность приложения имеет решающее значение. Было бы здорово, если бы кто-то, кто был на этом пути раньше, мог бы дать совет. Не слишком ли я придираюсь к тому, чтобы оптимизировать?
источник
Ответы:
Современные компиляторы JVM / JIC убрали снижение производительности большинства традиционно «медленных» операций, включая instanceof, обработку исключений, отражение и т. Д.
Как писал Дональд Кнут, «мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация - корень всего зла». Производительность instanceof, вероятно, не будет проблемой, так что не тратьте свое время на экзотические обходные пути, пока не убедитесь, что это проблема.
источник
try { ObjT o = (ObjT)object } catch (e) { no not one of these }
бы немного медленнее?Подходить
Я написал тестовую программу для оценки различных реализаций:
instanceof
реализация (как ссылка)@Override
метод тестированияgetClass() == _.class
реализацияЯ использовал jmh для запуска теста с 100 разминочными вызовами, 1000 итераций при измерении и с 10 форками. Таким образом, каждая опция была измерена с 10 000 раз, что требует 12:18:57 для запуска всего теста на моем MacBook Pro с macOS 10.12.4 и Java 1.8. Тест измеряет среднее время каждого варианта. Для более подробной информации смотрите мою реализацию на GitHub .
Для полноты: есть предыдущая версия этого ответа и мой тест .
Полученные результаты
ТЛ; др
В Java 1.8
instanceof
самый быстрый подход, хотяgetClass()
и очень близко.источник
+0.(9)
для науки!+1.0(9)
. :)System.currentTimeMillis()
операции, которая не намного больше, чем один вызов метода, что должно дать большую точность. Вместо этого используйте тестовую среду, такую как JMH !Я только что сделал простой тест, чтобы увидеть, как производительность instanceOf сравнивается с простым вызовом s.equals () строкового объекта с одной буквой.
в цикле 10.000.000 instanceO дал мне 63-96 мс, а строка равных дала мне 106-230 мс
Я использовал Java JVM 6.
Так что в моем простом тесте быстрее сделать instanceOf вместо сравнения строк из одного символа.
использование Integer .equals () вместо строковых значений дало мне тот же результат, только когда я использовал == я был быстрее, чем instanceOf на 20 мс (в цикле 10.000.000)
источник
equals()
не будет сокращать это, потому что подклассы; вам нужноisAssignableFrom()
.Элементы, которые будут определять влияние на производительность:
Я создал микробенчмарк для четырех разных способов отправки . Результаты Solaris следующие: меньшее число быстрее:
источник
Отвечая на ваш последний вопрос: если только профайлер не скажет вам, что вы тратите смешное количество времени в экземпляре: да, вы придирчивы.
Прежде чем задуматься об оптимизации чего-то, что никогда не нужно было оптимизировать: напишите свой алгоритм наиболее читабельным способом и запустите его. Запускайте его, пока jit-компилятор не получит возможность оптимизировать его самому. Если после этого у вас возникнут проблемы с этим фрагментом кода, используйте профилировщик, чтобы сообщить вам, где получить максимальную выгоду и оптимизировать это.
Во времена высокооптимизирующих компиляторов ваши догадки о узких местах, скорее всего, будут полностью неверными.
И в истинном духе этого ответа (в который я искренне верю): я абсолютно не знаю, как соотносятся instanceof и ==, как только jit-компилятор получает возможность его оптимизировать.
Я забыл: никогда не измеряй первый прогон.
источник
У меня тот же вопрос, но поскольку я не нашел «метрики производительности» для варианта использования, аналогичного моему, я сделал еще несколько примеров кода. На моем оборудовании и Java 6 & 7 разница между instanceof и переключением на 10 миллионов итераций
Таким образом, instanceof действительно медленнее, особенно для огромного числа операторов if-else-if, однако в реальном приложении разница будет незначительной.
источник
instanceof
это действительно быстро, принимая всего несколько инструкций процессора.Очевидно, что если в классе
X
нет загруженных подклассов (JVM знает),instanceof
его можно оптимизировать следующим образом:Основная стоимость только чтение!
Если
X
есть загруженные подклассы, требуется еще несколько чтений; они, вероятно, расположены рядом, поэтому дополнительные расходы тоже очень низкие.Хорошие новости всем!
источник
foo
- но наfoo
самом деле в настоящее время оптимизируется с помощью Oracle javac / VM - или просто возможно, что он сделает это в будущем? Кроме того, я спросил ответчика, есть ли у него какой-либо вспомогательный источник (будь то документы, исходный код, блог разработчика), документирующий, что он действительно может быть оптимизирован или оптимизирован ? Без этого этот ответ - просто случайное размышление о том, что может сделать компилятор .Экземпляр очень быстрый. Это сводится к байт-коду, который используется для сравнения ссылок на классы. Попробуйте несколько миллионов instanceofs в цикле и убедитесь сами.
источник
instanceof, вероятно, будет более дорогостоящим, чем простое равенство в большинстве реализаций реального мира (то есть тех, где instanceof действительно необходим, и вы не можете просто решить его путем переопределения общего метода, как любой учебник для начинающих, а также Демиан выше предлагаю).
Это почему? Потому что, вероятно, произойдет то, что у вас есть несколько интерфейсов, которые предоставляют некоторую функциональность (скажем, интерфейсы x, y и z) и некоторые объекты для манипулирования, которые могут (или нет) реализовывать один из этих интерфейсов ... но не напрямую. Скажем, например, у меня есть:
ш расширяет х
А реализует ш
Б расширяет А
C расширяет B, реализует у
D расширяет C, реализует Z
Предположим, я обрабатываю экземпляр D, объект d. Для вычислений (d instanceof x) требуется взять d.getClass (), пройтись по циклам через интерфейсы, которые он реализует, чтобы узнать, равен ли он == x, и если нет, сделать это снова рекурсивно для всех их предков ... В нашем случае, если вы в первый раз исследуете это дерево, вы получите как минимум 8 сравнений, предположив, что y и z ничего не расширяют ...
Сложность дерева деривации в реальном мире, вероятно, будет выше. В некоторых случаях JIT может оптимизировать большую часть этого, если он может заранее разрешить d как во всех возможных случаях экземпляр чего-то, что расширяет x. Реально, однако, вы будете проходить через это дерево большую часть времени.
Если это станет проблемой, я бы предложил вместо этого использовать карту обработчика, связав конкретный класс объекта с замыканием, которое выполняет обработку. Это удаляет фазу обхода дерева в пользу прямого отображения. Однако помните, что если вы установили обработчик для C.class, мой объект d выше не будет распознан.
вот мои 2 цента, надеюсь они помогут ...
источник
instanceof очень эффективен, поэтому ваша производительность вряд ли пострадает. Тем не менее, использование большого количества instanceof наводит на мысль о проблеме дизайна.
Если вы можете использовать xClass == String.class, это быстрее. Примечание: вам не нужен instanceof для выпускных классов.
источник
x.getClass() == Class.class
же, какx instanceof Class
x
быть,null
я полагаю. (Или что будет яснее)Как правило, причина, по которой оператор instanceof не одобряется в подобном случае (где instanceof проверяет подклассы этого базового класса), заключается в том, что вам нужно переместить операции в метод и переопределить его для соответствующего подклассы. Например, если у вас есть:
Вы можете заменить это на
и затем иметь реализацию "doEverything ()" в вызове Class1 "doThis ()", а в классе 2 вызов "doThat ()" и так далее.
источник
'instanceof' на самом деле является оператором, как + или -, и я считаю, что у него есть собственная инструкция байт-кода JVM. Это должно быть достаточно быстро.
Я не должен делать так, что если у вас есть переключатель, где вы тестируете, является ли объект экземпляром некоторого подкласса, то ваш дизайн, возможно, придется переделать. Подумайте о том, чтобы внедрить специфичное для подкласса поведение в сами подклассы.
источник
Демиан и Пол упоминают хороший момент; Тем не менее , размещение кода для выполнения действительно зависит от того, как вы хотите использовать данные ...
Я большой поклонник небольших объектов данных, которые можно использовать по-разному. Если вы используете переопределенный (полиморфный) подход, ваши объекты можно использовать только «в одну сторону».
Это где шаблоны приходят ...
Вы можете использовать двойную диспетчеризацию (как в шаблоне посетителя), чтобы попросить каждый объект «позвонить вам», передавая себя - это разрешит тип объекта. Однако (опять же) вам понадобится класс, который может «делать вещи» со всеми возможными подтипами.
Я предпочитаю использовать шаблон стратегии, где вы можете зарегистрировать стратегии для каждого подтипа, который вы хотите обработать. Что-то вроде следующего. Обратите внимание, что это помогает только для точных совпадений типов, но имеет то преимущество, что оно расширяемое - сторонние участники могут добавлять свои собственные типы и обработчики. (Это хорошо для динамических сред, таких как OSGi, где могут быть добавлены новые пакеты)
Надеюсь, это вдохновит на другие идеи ...
источник
Я пишу тест производительности на основе jmh-java-benchmark-archetype: 2.21. JDK - openjdk, версия 1.8.0_212. Тестовая машина Mac Pro. Результат теста:
Результат показывает, что: getClass лучше, чем instanceOf, что противоречит другому тесту. Однако я не знаю почему.
Тестовый код ниже:
источник
Трудно сказать, как определенная JVM реализует экземпляр, но в большинстве случаев Объекты сравнимы со структурами, а также с классами, и каждая структура объекта имеет указатель на структуру класса, экземпляром которой он является. Так на самом деле instanceof для
может быть так же быстро, как следующий код C
предполагая, что JIT-компилятор на месте и делает достойную работу.
Учитывая, что это только доступ к указателю, получение указателя с определенным смещением, на которое указывает указатель, и сравнение его с другим указателем (что в основном совпадает с проверкой на равенство 32-битных чисел), я бы сказал, что операция на самом деле может быть очень быстрым
Это не обязательно, однако, это во многом зависит от JVM. Однако, если это окажется узким местом в вашем коде, я бы посчитал реализацию JVM довольно плохой. Даже тот, у которого нет JIT-компилятора и только интерпретирует код, должен иметь возможность сделать экземпляр теста практически мгновенно.
источник
Я свяжусь с вами по поводу выступления. Но способ избежать проблемы (или ее отсутствия) вообще состоит в том, чтобы создать родительский интерфейс для всех подклассов, на которых вам нужно сделать instanceof. Интерфейс будет супер-набором всех методов в подклассах, для которых вам нужно будет выполнить instanceof check. Если метод не применяется к конкретному подклассу, просто предоставьте фиктивную реализацию этого метода. Если я не понял проблему неправильно, то вот как я обошел проблему в прошлом.
источник
InstanceOf - предупреждение о плохом объектно-ориентированном дизайне.
Текущие JVM означают, что instanceOf сам по себе не является проблемой производительности. Если вы часто используете его, особенно для основных функций, возможно, пришло время взглянуть на дизайн. Прирост производительности (и простоты / ремонтопригодности) от рефакторинга к лучшему дизайну значительно перевесит любые фактические циклы процессора, потраченные на фактический вызов instanceOf .
Чтобы привести очень маленький пример упрощенного программирования.
Является ли плохая архитектура лучшим выбором, если бы SomeObject был родительским классом двух дочерних классов, где каждый дочерний класс переопределяет метод (doSomething), поэтому код будет выглядеть так:
источник
В современной версии Java оператор instanceof работает быстрее, чем простой вызов метода. Это означает:
быстрее как:
Другое дело, если вам нужно каскадировать много экземпляров. Тогда переключатель, который вызывает только один раз getType (), быстрее.
источник
Если ваша единственная цель - скорость, то использование int-констант для идентификации подклассов, похоже, экономит миллисекунды времени.
ужасный дизайн ОО, но если ваш анализ производительности показывает, что это то, где вы узкое место, то, возможно,. В моем коде код отправки занимает 10% от общего времени выполнения, и это может способствовать повышению общей скорости на 1%.
источник
Вы должны измерить / профиль, если это действительно проблема производительности в вашем проекте. Если это так, я бы порекомендовал редизайн - если это возможно. Я почти уверен, что вы не можете превзойти нативную реализацию платформы (написано на C). Вы должны также рассмотреть множественное наследование в этом случае.
Вы должны рассказать больше о проблеме, возможно, вы могли бы использовать ассоциативное хранилище, например Map <Class, Object>, если вас интересуют только конкретные типы.
источник
Что касается замечания Питера Лоури, что вам не нужен instanceof для выпускных классов и вы можете просто использовать равенство ссылок, будьте осторожны! Хотя конечные классы не могут быть расширены, они не гарантированно загружаются одним и тем же загрузчиком классов. Используйте x.getClass () == SomeFinal.class или тому подобное, только если вы абсолютно уверены, что для этой части кода в игре только один загрузчик классов.
источник
Я также предпочитаю подход enum, но я бы использовал абстрактный базовый класс, чтобы заставить подклассы реализовать
getType()
метод.источник
Я подумал, что, возможно, стоит представить контрпример к общему мнению на этой странице, что «instanceof» не настолько дорог, чтобы о нем беспокоиться. Я обнаружил, что у меня есть некоторый код во внутреннем цикле, который (в какой-то исторической попытке оптимизации) сделал
где вызов head () для SingleItem возвращает значение без изменений. Замена кода на
дает мне ускорение с 269 мс до 169 мс, несмотря на то, что в цикле происходят довольно тяжелые вещи, такие как преобразование строки в двойное. Конечно, возможно, что ускорение больше связано с устранением условного перехода, чем с удалением самого экземпляра оператора; но я думал, что стоит упомянуть.
источник
if
себя. Если распределениеtrue
s иfalse
s близко к четному, умозрительное исполнение становится бесполезным, что приводит к значительным задержкам.Вы сосредоточены на неправильной вещи. Разница между instanceof и любым другим методом проверки того же самого, вероятно, даже не поддается измерению. Если производительность критична, то Java, вероятно, не тот язык. Основная причина в том, что вы не можете контролировать, когда виртуальная машина решает, что она хочет собирать мусор, что может привести к увеличению загрузки ЦП до 100% в течение нескольких секунд в большой программе (MagicDraw 10 отлично подходит для этого). Если вы не контролируете каждый компьютер, на котором будет работать эта программа, вы не сможете гарантировать, на какой версии JVM она будет работать, и у многих из старых были серьезные проблемы со скоростью. Если это маленькое приложение, вы можете быть в порядке с Java, но если вы постоянно читаете и отбрасываете данные, вы заметите, когда GC начнет работу .
источник