Якщо говорити про продуктивність:
TL; DR
Використовуйте isInstance або instanceof, які мають аналогічні показники. Визначення від цього трохи повільніше.
Сортування за продуктивністю:
- isInstance
- instanceof (+ 0,5%)
- єдоступним від (+ 2,7%)
Заснований на еталоні 2000 ітерацій на JAVA 8 Windows x64, з 20 ітераціями розминки.
Теоретично
Використовуючи програмний перегляд байт-коду, ми можемо перевести кожного оператора в байт-код.
В контексті:
package foo;
public class Benchmark
{
public static final Object a = new A();
public static final Object b = new B();
...
}
JAVA:
b instanceof A;
Байт-код:
getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A
JAVA:
A.class.isInstance(b);
Байт-код:
ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Class isInstance((Ljava/lang/Object;)Z);
JAVA:
A.class.isAssignableFrom(b.getClass());
Байт-код:
ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Object getClass(()Ljava/lang/Class;);
invokevirtual java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z);
Виміряючи, скільки інструкцій байтового коду використовується кожним оператором, можна очікувати, що instanceof та isInstance будуть швидшими, ніж isAssignableFrom . Однак фактична продуктивність визначається НЕ байт-кодом, а машинним кодом (який залежить від платформи). Давайте зробимо мікро-орієнтир для кожного з операторів.
Орієнтир
Кредит: Як радив @ aleksandr-dubinsky, і завдяки @yura за надання базового коду, ось орієнтир JMH (див. Цей посібник з настройки ):
class A {}
class B extends A {}
public class Benchmark {
public static final Object a = new A();
public static final Object b = new B();
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testInstanceOf()
{
return b instanceof A;
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testIsInstance()
{
return A.class.isInstance(b);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testIsAssignableFrom()
{
return A.class.isAssignableFrom(b.getClass());
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(TestPerf2.class.getSimpleName())
.warmupIterations(20)
.measurementIterations(2000)
.forks(1)
.build();
new Runner(opt).run();
}
}
Дайте такі результати (оцінка - це кількість операцій у одиниці часу , тому чим більша оцінка, тим краще):
Benchmark Mode Cnt Score Error Units
Benchmark.testIsInstance thrpt 2000 373,061 ± 0,115 ops/us
Benchmark.testInstanceOf thrpt 2000 371,047 ± 0,131 ops/us
Benchmark.testIsAssignableFrom thrpt 2000 363,648 ± 0,289 ops/us
Увага
- еталоном є JVM і залежать від платформи. Оскільки між кожною операцією немає суттєвих відмінностей, можливо, можна отримати інший результат (а може бути і інший порядок!) На іншій версії JAVA та / або платформах, таких як Solaris, Mac або Linux.
- тест порівнює ефективність "є B екземпляр A", коли "B поширює A" безпосередньо. Якщо ієрархія класів глибша і складніша (як, наприклад, B подовжує X, яка Y, яка Z, яка A, A), результати можуть бути різними.
- Зазвичай рекомендується написати код, вибираючи одного з операторів (найзручніший), а потім профілюючи свій код, щоб перевірити, чи є вузькі місця. Можливо, цей оператор є мізерним у контексті вашого коду, а може ...
- стосовно попереднього пункту,
instanceof
в контексті вашого коду можна оптимізувати простіше, ніж isInstance
наприклад ...
Щоб навести приклад, візьміть таку петлю:
class A{}
class B extends A{}
A b = new B();
boolean execute(){
return A.class.isAssignableFrom(b.getClass());
// return A.class.isInstance(b);
// return b instanceof A;
}
// Warmup the code
for (int i = 0; i < 100; ++i)
execute();
// Time it
int count = 100000;
final long start = System.nanoTime();
for(int i=0; i<count; i++){
execute();
}
final long elapsed = System.nanoTime() - start;
Завдяки JIT код оптимізується в якийсь момент, і ми отримуємо:
- instanceof: 6ms
- isInstance: 12ms
- Доступно від: 15 мс
Примітка
Спочатку ця публікація робила свій власний орієнтир, використовуючи цикл for raw в JAVA, який дав недостовірні результати, оскільки певна оптимізація, як Just In Time, може усунути цикл. Так що в основному вимірювання , скільки часу зробив взяття JIT компілятор для оптимізації циклу: см тест продуктивності залежить від числа ітерацій для отримання більш докладної інформації
Пов'язані питання