V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hackingwu
V2EX  ›  程序员

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

  •  1
     
  •   hackingwu ·
    hackingwu · 2019-06-26 15:02:05 +08:00 · 3621 次点击
    这是一个创建于 1981 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码:

      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 方法,差异这么大,有办法改善吗?

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


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

    可以比你现有代码的反射提升一倍的效率,再要提升效率,就木有办法了。。。其实反射和方法调用的性能差距已经在微秒级了,很多场合可以忽略这种差距。
    lingnin
        8
    lingnin  
       2019-06-26 17:18:48 +08:00
    别用 java
    firefffffffffly
        9
    firefffffffffly  
       2019-06-26 17:40:08 +08:00
    性能有差距,但是应该不会像例子里这么大, 这段代码做 benchmark 不太严谨,比如第二段代码可能会被 jit 优化到没有。
    依照这个测试 https://dzone.com/articles/the-performance-cost-of-reflection 不包含具体逻辑的情况下测试大约是差 10 倍。
    luozic
        10
    luozic  
       2019-06-26 17:47:41 +08:00 via iPhone
    缓存
    mind3x
        11
    mind3x  
       2019-06-26 17:56:03 +08:00   ❤️ 14
    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
        12
    MotherShip  
       2019-06-26 18:05:43 +08:00
    @mind3x 讲的很详细。。我有点想提醒楼主 JIT 的优化但是讲不出所以然来
    guixiexiezou
        13
    guixiexiezou  
       2019-06-26 18:10:11 +08:00
    楼上说的很清楚了。差距是有,但不会这么大,大概是 47 倍到 147 之间
    wdlth
        14
    wdlth  
       2019-06-26 23:14:53 +08:00
    试试 ReflectASM
    troywinter
        15
    troywinter  
       2019-06-27 20:55:47 +08:00
    #11 楼正解,java 的 microbenchmark 很重要,做跑分一定要有足够的预热,不然你测出来的结果是比 python 还慢,然而真实情况并不是这样,microbenchmark 是 java 开发者的基础。
    crazyweeds
        16
    crazyweeds  
       2021-05-13 22:04:13 +08:00
    @mind3x 活捉一头大佬
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3117 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 14:11 · PVG 22:11 · LAX 06:11 · JFK 09:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.