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

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

  •  1
     
  •   hackingwu · 144 天前 · 2288 次点击
    这是一个创建于 144 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码:

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

    15 回复  |  直到 2019-06-27 20:55:47 +08:00
        1
    leavic   144 天前
    不好意思误入了,我以为你在讨论 RF 的反射。。。
        2
    Lin0936   144 天前
    @leavic #1 乳房还能反射?
        3
    BingoXuan   144 天前
    @Lin0936
    ls 应该说的是射频吧
        4
    cjlmwcy   144 天前
    实际操作的时候可以把方法缓存起来,会好一些
        5
    cjlmwcy   144 天前
    @cjlmwcy 没认真审题,我错了
        6
    gz911122   144 天前


    比如 apt,编译期间生成代码
    再比如 aspectj 等字节码织入的
        7
    guyeu   144 天前
    ```java
    method.setAccessible(true);
    ```

    可以比你现有代码的反射提升一倍的效率,再要提升效率,就木有办法了。。。其实反射和方法调用的性能差距已经在微秒级了,很多场合可以忽略这种差距。
        8
    lingnin   144 天前
    别用 java
        9
    firefffffffffly   144 天前
    性能有差距,但是应该不会像例子里这么大, 这段代码做 benchmark 不太严谨,比如第二段代码可能会被 jit 优化到没有。
    依照这个测试 https://dzone.com/articles/the-performance-cost-of-reflection 不包含具体逻辑的情况下测试大约是差 10 倍。
        10
    luozic   144 天前 via iPhone
    缓存
        11
    mind3x   144 天前   ♥ 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 万次,区别也只有千分之一。
        12
    MotherShip   144 天前
    @mind3x 讲的很详细。。我有点想提醒楼主 JIT 的优化但是讲不出所以然来
        13
    guixiexiezou   144 天前
    楼上说的很清楚了。差距是有,但不会这么大,大概是 47 倍到 147 之间
        14
    wdlth   144 天前
    试试 ReflectASM
        15
    troywinter   143 天前
    #11 楼正解,java 的 microbenchmark 很重要,做跑分一定要有足够的预热,不然你测出来的结果是比 python 还慢,然而真实情况并不是这样,microbenchmark 是 java 开发者的基础。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   4041 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 24ms · UTC 06:50 · PVG 14:50 · LAX 22:50 · JFK 01:50
    ♥ Do have faith in what you're doing.