关于java:记一次程序优化历程

4次阅读

共计 1229 个字符,预计需要花费 4 分钟才能阅读完成。

用 java 写了一个游戏计算最高得分的工具,游戏规则是,一个厨师能够带一个厨具,做三种菜谱,而后能够上场三个厨师。厨师和厨具别离有技能,技能有许多种成果,有些非凡的还能影响其余厨师,菜谱有技法要求限度。计算就是用 3 个厨师,3 个厨具,9 个菜谱,计算得分。

厨师有 60 多个,菜谱 200 多,厨具几十个。
首先想到了动静布局,然而每个菜谱须要耗费肯定数量食材,每种食材都有数量限度,前一个菜谱的抉择会影响后边的后果,厨师,厨具,菜谱能够做的数量,维度太多,搜寻空间太大,没想出思路。

最初想到的思路是,生成 9 个菜谱的有序组合 (安菜谱价格由高到低,生成),3 个厨师的无序组合,三个厨具的有序组合。
菜谱组合数 41989
厨师组合数 3095
厨具组合数 79079

而后先组合厨师和菜谱,保留得分最高的 3k 个,而后再组合厨具,从新计算得分。我只应用前 1000 个菜谱组合,在应用 i7-6700HK 的 cpu 上计算工夫也要十几分钟。

最开始认为是算法写的烂,三个维度数据量太大。而后理解到 idea 的 profiler 工具能够用于性能剖析。

剖析后果后果如图

能够从图中看到,对一些函数的 cpu 应用状况有一个统计,左侧是函数的 cpu 应用占比。然而统计的不全,应该是一些非函数调用的语句没有统计。

最开始一个线程,30 个厨师组合,1000 个有序菜谱组合,一次计算耗时是 50s 左右,应用 profiler 剖析 cpu 耗时,发现是计算厨师技能的函数耗时占比 30%

这个函数是计算三个厨师的技法加成,以及技法对其余厨师的影响,大量的加法运算,导致耗时长。其实这个函数只和厨师组合无关,所以厨师组合确定,技法成果也应该确定了,然而我的每一组厨师和每一组菜谱(有序组合变无序为 1680 个)的搭配都要调用该办法,也就是反复计算了 1680 次。在没应用 profiler 前,还真不知道大部分耗时在这里。

优化后耗时变为 30s,

而后持续测试,发现这次 ArrayList 的 subList 办法和 get 办法耗时占大头,
因为我将长度为 9 的菜谱组合放在 ArrayList 中,当初要拆成 3 个 3 等分,所以应用了 subList,最初改为应用二维数组装三等分的菜谱。

优化后耗时变为 21s 左右。

说到这里有个很神奇的景象,我的性能测试在 window10 下,别离应用 openjdk8,oraclejdk11,oraclejdk15,GraalVM CE 20.2.0,下测试,除了 GraalVM CE 20.2.0,其余的都稳固在 20s 左右,唯独 GraalVM,最初能优化到 14s 左右,而我发现这个优化会和一个 for 循环无关,

14 秒耗时版本

20 秒耗时版本

不出意外,我不懂汇编,没有自卑到剖析汇编后的代码,然而感觉和 Link 无关,于是把关涉大量计算局部的 LinK 都改为了对象数组。

后果执行工夫缩小到 7s 左右。当我兴奋的应用 openjdk8,oraclejdk11,oraclejdk15 测试的时候,发现没有变动,还是 20 秒左右,只有 GraalVM 稳固在 7 秒。

mmp…

正文完
 0