Object.isArray() is slow, is there a fast way to do that?

匿名 (未验证) 提交于 2019-12-03 01:33:01

问题:

In my application obj.getClass().isArray() is called very frequently and become the bottleneck of the app.
I want to check efficiently at run-time if an object is an array. Primitive array and object array should return true.
The way I can imagine is to instanceof all primtive arrays, but cannot handle types like int[][]. And the app is used as lib, so I cannot list all types.
Is there any clue for that?

回答1:

A benchmark I've just done gave the following results:

{s instanceof Object[]} spends 44ms {s.getClass().getName().charAt(0) == '['} spends 58ms {s.getClass().isArray()} spends 303ms 

Benchmark has been done using Benchmark.java, called with Main.java.


After having discussed the use of a final variable in the above benchmark, see the new results using a local one:

{s instanceof Object[]} spends 83ms {s.getClass().getName().charAt(0) == '['} spends 93ms {s.getClass().isArray()} spends 354ms 

Even if the durations are all a bit longer (interesting btw), their order has been preserved.

Benchmark.java has been then called with this new Main.java.


And using a primitive array called with this other Main.java:

{a instanceof int[]} spends 71ms {a.getClass().getName().charAt(0) == '['} spends 82ms {a.getClass().isArray()} spends 340ms 

Still the same results order.



回答2:

isArray() is the most efficient way to check if an object is an instance of an array at runtime. If the performance is a problem, you can use one of the following methods to address it:

  • Refactor your code so array objects and non-array objects are handled separately, so the results of isArray() are known at compile time.
  • Use local variables and/or arguments to cache the value of isArray() during an operation, so it only needs to be called once.


回答3:

From your comments, I conclude that you may be suffering from an interpretive mistake when investigating profiling results. Your profiler's method-level instrumentation might be heavily crippling getClass() and isArray() calls, while being unimpressed by instanceof expressions. In other words, you're probably measuring the measuring overhead of your profiler, here.

Besides, in a quick benchmark, I cannot back your claim. I've run the following, very silly test:

public class Test {     public static void main(String[] args) {         final int rep = 10000000;         Object[] o = {             null,             1,             "x",             new Object[0],             new Object[0][],             new int[0],             new int[0][]         };          // "Warmup" to avoid potential JVM startup overhead         long x = 0;         for (int i = 0; i < rep; i++) {             x+=checkInstanceOf(o);         }          for (int i = 0; i < rep; i++) {             x+=checkIsArray(o);         }          for (int i = 0; i < rep; i++) {             x+=checkClassName(o);         }          // Actual test         long t1 = System.nanoTime();         for (int i = 0; i < rep; i++) {             x+=checkInstanceOf(o);         }          long t2 = System.nanoTime();         for (int i = 0; i < rep; i++) {             x+=checkIsArray(o);         }          long t3 = System.nanoTime();         for (int i = 0; i < rep; i++) {             x+=checkClassName(o);         }          long t4 = System.nanoTime();          System.out.println(t2 - t1);         System.out.println(t3 - t2);         System.out.println(t4 - t3);     }      private static int checkInstanceOf(Object[] o) {         int i = 0;         for (Object x : o) {             if (x instanceof Object[]) i++;       // Perform some logic             else if (x instanceof boolean[]) i++; // to keep the compiler or             else if (x instanceof byte[]) i++;    // the JVM from optimising             else if (x instanceof short[]) i++;   // this code away             else if (x instanceof int[]) i++;             else if (x instanceof long[]) i++;             else if (x instanceof float[]) i++;             else if (x instanceof double[]) i++;             else if (x instanceof char[]) i++;         }         return i;     }      private static int checkIsArray(Object[] o) {         int i = 0;         for (Object x : o) {             if (x != null && x.getClass().isArray()) i++;         }         return i;     }      private static int checkClassName(Object[] o) {         int i = 0;         for (Object x : o) {             if (x != null && x.getClass().getName().charAt(0) == '[') i++;         }         return i;     } } 

I'm getting:

394433000 // instanceof 110655000 // getClass().isArray() 396039000 // getClass().getName().charAt(0) == '[' 

So you cannot generally claim getClass().isArray() to be slower than a thorough set of instanceof checks. Of course, there is a lot of different ways to rewrite my test, but you get the idea.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!