場景 : 最近,有客戶反應某些功能執行得很慢,我們於是對代碼日志進行了定位,我們的系統架構是nginx+tomcat; 我們可以直接定位到tomcat的catalina日志,但是后來吧,我們這邊統一要整理響應時間超過5S以上的,對這些都要進行整改;所以我們先直接分析nginx的日志文件,查看請求跟響應超過5S以上的統計出來,然后在tomcat的日志當中定位這些請求,查找到具體的時間,以及上下文,最后我們定位到某個方法執行超過一些時間的(比如一個方法超過2S了,這肯定不行啦)。
現在,我們拋開掉業務場景,從最近本的優化方案看。
轉載自 :https://www.cnblogs.com/snifferhu/p/4600321.html
1. 嵌套循環應該內大外小,還是內小外大?
內大外小指的是 : 內層循環比外層循環次數多。
代碼實例 :
@Test public void testForNeiWai() { // 方法說明 : 測試內外循環 // 1. 測試外循環比內循環大 Long startTime = System.nanoTime(); for (int i = 0; i<1000000; i++){ for ( int j = 0; j<100 ; j++){ } } Long endTime = System.nanoTime(); System.out.println("外大內小耗時: "+(endTime-startTime)); }
這里我們運行3次,取3次結果的平均值 : (我這里是8G內存,普通的筆記本,沒有SSD什么的)
運行時間為 : 外大內小耗時: 325459668; 外大內小耗時: 168779176; 外大內小耗時: 317150703;
平均耗時為 : 270463182.33333~
接下來,我們看下,外小內大的情況:
@Test public void testForWaiNei() { // 方法說明 : 測試內外循環 // 1. 測試外循環比內循環小 Long startTime = System.nanoTime(); for (int i = 0; i<100; i++){ for ( int j = 0; j<1000000 ; j++){ } } Long endTime = System.nanoTime(); System.out.println("內大外小耗時: "+(endTime-startTime)); }
運行的結果是:內大內外耗時: 355928863; 內大內外耗時: 353038045; 內大內外耗時: 352683093;
結果為 : 353883333.666~
由此可見,其實相差這么多倍其實差距並不是太大。雖然網上要求我們堅持外小內大的原則,實際上可能應該按當時的業務場景+測試來得到最終的方案吧。
2. 提取與循環無關的表達式
@Test public void testForWuGuan() { // 方法說明 : 測試內外循環 // 1. 測試外循環比內循環小 int a = 10,b=12; Long startTime = System.nanoTime(); for ( int j = 0; j<1000000 ; j++){ j = j*a*b; } Long endTime = System.nanoTime(); System.out.println("耗時: "+(endTime-startTime)); }
同理,運行的結果是: 內大內外耗時: 21758;內大內外耗時: 14931; 內大內外耗時: 17065
我們大致定位到這是 18000;我們再看下把a*b提取出去之后的 :
同理,運行時間是:耗時: 13652;耗時: 15785;耗時: 14078;這個耗時現在看是減少了一些,然后如果你這個里面有很多這種無關循環的表達式的話,那確實可以提升性能。
3. 消除循環終止判斷時的方法調用
@Test public void testForWuXunHuanBianLiang() { String sql = "select * from in_applyinfo where status_lookup_code='140' allow filtering"; List<ApplyInfoMigration> applyInfoMigrationList = applyInfoDao.selectList(sql,ApplyInfoMigration.class ); // A實體 int a = 10,b=12, c=a*b; Long startTime = System.nanoTime(); for ( int j = 0; j<applyInfoMigrationList.size() ; j++){ // j = j*c; } Long endTime = System.nanoTime(); System.out.println("耗時: "+(endTime-startTime)); }
同理 : 耗時: 37116;耗時: 34556; 耗時: 38397;平均:37000;現在讓我們把上面的list.size()使用一個常量來代替,然后看執行結果是多少。
同理: 耗時: 14505;耗時: 17492; 耗時: 14506;由此可見,此性能提升了多少,相信大家都有數了,不要讓這一點點代碼而影響我們這么多性能。
4. 對異常進行優化
@Test public void testForException() { // 方法說明 : 測試for循環優化 Long startTime = System.nanoTime(); for ( int j = 0; j<1000000 ; j++){ try { }catch (Exception e){ } } Long endTime = System.nanoTime(); System.out.println("耗時: "+(endTime-startTime)); }
同理 : 耗時: 4753466; 耗時: 6354166; 耗時: 5265843。接下來,我們把try-catch語句塊移到外面 :
@Test public void testForExceptionTwo() { // 方法說明 : 測試for循環優化 Long startTime = System.nanoTime(); try { for ( int j = 0; j<1000000 ; j++){ } }catch (Exception e){ } Long endTime = System.nanoTime(); System.out.println("耗時: "+(endTime-startTime)); }
同理 : 耗時: 4925822 ;耗時: 5144255; 耗時: 5748358; 若我們把代碼放到一堆比較的話 :
耗時: 7290610 (catch在里面)、4804234
耗時: 3249610 (catch在外面) 、3632720; 從我這里看性能並沒有多大的提升,可能是也沒有中間業務處理流程,也可能是個人的junit測試環境問題,總之,感覺這里並沒太大的提升。不過對於上面的我覺得可能是jdk1.8版本所導致的吧,所以還是根據個人使用環境來,那么我這里就采取把list的size變為常量來吧。
5. 說完了上面的for循環基本的優化之后,我覺得實際上還是各自測試下,按照自己當前的業務跟jdk版本來確定優化策略,然后下面我想說一下的是把for循環變為多線程來優化。
--> 后續補充
