反射性能差这么多,有办法提高吗?

2019-06-26 15:02:05 +08:00
 hackingwu

代码:

  public static void test() {
    int i = 0;
  }
  public static void main(String[] args)
      throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    int times = Integer.MAX_VALUE;
    Method method = ReflectTest.class.getMethod("test", null);
    long start = System.currentTimeMillis();
    while (times-->0) {
      method.invoke(ReflectTest.class);
    }
    long end1 = System.currentTimeMillis();

    while (++times<Integer.MAX_VALUE) {
      test();
    }
    long end2 = System.currentTimeMillis();
    System.out.println("end1 - start: " + (end1-start)+", end2 - end1: "+(end2-end1));
  }

结果:end1 - start: 9137, end2 - end1: 1 查了千倍,Method 方法已经算是缓存,而且不在计时之内。计时只是 invoke 方法,差异这么大,有办法改善吗?

3605 次点击
所在节点    程序员
16 条回复
leavic
2019-06-26 15:03:50 +08:00
不好意思误入了,我以为你在讨论 RF 的反射。。。
Lin0936
2019-06-26 15:09:57 +08:00
@leavic #1 乳房还能反射?
BingoXuan
2019-06-26 15:12:42 +08:00
@Lin0936
ls 应该说的是射频吧
cjlmwcy
2019-06-26 15:15:19 +08:00
实际操作的时候可以把方法缓存起来,会好一些
cjlmwcy
2019-06-26 15:16:10 +08:00
@cjlmwcy 没认真审题,我错了
gz911122
2019-06-26 15:16:47 +08:00


比如 apt,编译期间生成代码
再比如 aspectj 等字节码织入的
guyeu
2019-06-26 16:35:05 +08:00
```java
method.setAccessible(true);
```

可以比你现有代码的反射提升一倍的效率,再要提升效率,就木有办法了。。。其实反射和方法调用的性能差距已经在微秒级了,很多场合可以忽略这种差距。
lingnin
2019-06-26 17:18:48 +08:00
别用 java
firefffffffffly
2019-06-26 17:40:08 +08:00
性能有差距,但是应该不会像例子里这么大, 这段代码做 benchmark 不太严谨,比如第二段代码可能会被 jit 优化到没有。
依照这个测试 https://dzone.com/articles/the-performance-cost-of-reflection 不包含具体逻辑的情况下测试大约是差 10 倍。
luozic
2019-06-26 17:47:41 +08:00
缓存
mind3x
2019-06-26 17:56:03 +08:00
2.6GHz 主频的 7 代 i5,MIPS(每秒执行百万条指令数)大约是 53K。

0 循环到 max int,循环次数是 2,147,483,647,假设每个循环只执行三条指令,大约是 6K 个百万条指令。

也就是说,一个什么也不做的从 0 到 max int 的循环,在 7 代 i5 上,大约应该花 0.1 秒这么个量级的时间。我们就放宽一点,给它再快个 10 倍,大约应该花 10ms 这个量级的时间。今天应该还没有一款 CPU 的单核 IPC 能达到 10 倍 i5 的水平。

你猜猜看你的第二个循环为啥 1ms 就跑完了?

因为 JIT 在跑了前面的几百或者几千次循环以后开始介入编译,发现你的 test()很小,应该内联进循环,然后就内联了。内联以后一看原来整个循环也啥也没做,就把循环也优化掉了。

而第一个循环,反射是实打实没法优化掉的。

这个故事告诉我们,microbenchmark 通常没什么鸟用。如果一定要做 microbenchmark,请至少正确使用 JMH,在代码里通过 JMH 的 API 强制添加副作用,避免不希望的优化发生。

另外,即使反射比普通调用真的慢一千倍,实际到你的产品里很可能也只有不到 1%的差距。打个比方,如果你的方法本身要花 1 秒,反射花 1 毫秒,直接调用花 1 微秒,把你的方法调用 1 万次,区别也只有千分之一。
MotherShip
2019-06-26 18:05:43 +08:00
@mind3x 讲的很详细。。我有点想提醒楼主 JIT 的优化但是讲不出所以然来
guixiexiezou
2019-06-26 18:10:11 +08:00
楼上说的很清楚了。差距是有,但不会这么大,大概是 47 倍到 147 之间
wdlth
2019-06-26 23:14:53 +08:00
试试 ReflectASM
troywinter
2019-06-27 20:55:47 +08:00
#11 楼正解,java 的 microbenchmark 很重要,做跑分一定要有足够的预热,不然你测出来的结果是比 python 还慢,然而真实情况并不是这样,microbenchmark 是 java 开发者的基础。
crazyweeds
2021-05-13 22:04:13 +08:00
@mind3x 活捉一头大佬

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/577656

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX