用java寫了一個遊戲計算最高得分的工具,遊戲規則是,一個廚師能夠帶一個廚具,作三種菜譜,而後能夠上場三個廚師。廚師和廚具分別有技能,技能有許多種效果,有些特殊的還能影響其餘廚師,菜譜有技法要求限制。計算就是用3個廚師,3個廚具,9個菜譜,計算得分。java
廚師有60多個,菜譜200多,廚具幾十個。
首先想到了動態規劃,可是每一個菜譜須要消耗必定數量食材,每種食材都有數量限制,前一個菜譜的選擇會影響後邊的結果,廚師,廚具,菜譜能夠作的數量,維度太多,搜索空間太大,沒想出思路。算法
最後想到的思路是,生成 9個菜譜的有序組合(安菜譜價格由高到低,生成),3個廚師的無序組合,三個廚具的有序組合。
菜譜組合數41989
廚師組合數3095
廚具組合數79079數組
而後先組合廚師和菜譜,保留得分最高的3k個,而後再組合廚具,從新計算得分。我只使用前1000個菜譜組合,在使用i7-6700HK的cpu上計算時間也要十幾分鍾。oracle
最開始覺得是算法寫的爛,三個維度數據量太大。而後瞭解到idea的profiler工具能夠用於性能分析。ide
分析結果結果如圖
函數
能夠從圖中看到,對一些函數的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...