5.今まで使ってきたfor loopをもっと早く使えますか?


5.今まで使ってきたfor loopをもっと早く使えますか?


1.条件文の速度は?


条件文はパフォーマンスにどのくらい影響しますか?まず、条件文にどのような内容があるかを見て、条件文がパフォーマンスに与える影響を理解しましょう.条件文の種類は以下の通りです.
  • if-else if-else
  • switch
  • if文ではブール形式の結果値しか使用できません.switch文はバイト、short、char、intの4種類の条件分岐しか使用できませんが、JDK 7からStringも使用できます.ifゲートで分岐を多くすると,多くの時間がかかると考えられる.if文条件の比較文で速度を遅くしないと、if文自体にあまり時間がかかりません.
    package com.perf.condition;
    
    import java.util.Random;
    import java.util.concurrent.TimeUnit;
    
    import org.openjdk.jmh.annotations.BenchmarkMode;
    import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
    import org.openjdk.jmh.annotations.Mode;
    import org.openjdk.jmh.annotations.OutputTimeUnit;
    import org.openjdk.jmh.annotations.Scope;
    import org.openjdk.jmh.annotations.State;
    
    @State(Scope.Thread)
    @BenchmarkMode({ Mode.AverageTime })
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public class ConditionIf {
        int LOOP_COUNT=1000;
    	
        @GenerateMicroBenchmark
        public void randomOnly() {
            Random random=new Random();
            int data=1000+random.nextInt();
            for(int loop=0;loop<LOOP_COUNT;loop++) {
                resultProcess("dummy");
            }
        }
        @GenerateMicroBenchmark
        public void if10() {
            Random random=new Random();
            String result=null;
            int data=1000+random.nextInt();
            for(int loop=0;loop<LOOP_COUNT;loop++) {
                if(data<50) { result="50";
                } else if(data<150) { result="150";
                } else if(data<250) { result="250";
                } else if(data<350) { result="350";
                } else if(data<450) { result="450";
                } else if(data<550) { result="550";
                } else if(data<650) { result="650";
                } else if(data<750) { result="750";
                } else if(data<850) { result="850";
                } else if(data<950) { result="950";
                } else { result="over";
                }
                resultProcess(result);
            }
        }
        @GenerateMicroBenchmark
        public void if100() {
            Random random=new Random();
            String result=null;
            int data=10000+random.nextInt();
            for(int loop=0;loop<LOOP_COUNT;loop++) {
                if(data<50) { result="50";
                } else if(data<150) { result="150";
                } else if(data<250) { result="250";
                } else if(data<350) { result="350";
                } else if(data<450) { result="450";
                } else if(data<550) { result="550";
                } else if(data<650) { result="650";
                } else if(data<750) { result="750";
                } else if(data<850) { result="850";
                } else if(data<950) { result="950";
                } else if(data<1050) { result="1050";
                } else if(data<1150) { result="1150";
                } else if(data<1250) { result="1250";
                } else if(data<1350) { result="1350";
                } else if(data<1450) { result="1450";
                } else if(data<1550) { result="1550";
                } else if(data<1650) { result="1650";
                } else if(data<1750) { result="1750";
                } else if(data<1850) { result="1850";
                } else if(data<1950) { result="1950";
                } else if(data<2050) { result="2050";
                } else if(data<2150) { result="2150";
                } else if(data<2250) { result="2250";
                } else if(data<2350) { result="2350";
                } else if(data<2450) { result="2450";
                } else if(data<2550) { result="2550";
                } else if(data<2650) { result="2650";
                } else if(data<2750) { result="2750";
                } else if(data<2850) { result="2850";
                } else if(data<2950) { result="2950";
                } else if(data<3050) { result="3050";
                } else if(data<3150) { result="3150";
                } else if(data<3250) { result="3250";
                } else if(data<3350) { result="3350";
                } else if(data<3450) { result="3450";
                } else if(data<3550) { result="3550";
                } else if(data<3650) { result="3650";
                } else if(data<3750) { result="3750";
                } else if(data<3850) { result="3850";
                } else if(data<3950) { result="3950";
                } else if(data<4050) { result="4050";
                } else if(data<4150) { result="4150";
                } else if(data<4250) { result="4250";
                } else if(data<4350) { result="4350";
                } else if(data<4450) { result="4450";
                } else if(data<4550) { result="4550";
                } else if(data<4650) { result="4650";
                } else if(data<4750) { result="4750";
                } else if(data<4850) { result="4850";
                } else if(data<4950) { result="4950";
                } else if(data<5050) { result="5050";
                } else if(data<5150) { result="5150";
                } else if(data<5250) { result="5250";
                } else if(data<5350) { result="5350";
                } else if(data<5450) { result="5450";
                } else if(data<5550) { result="5550";
                } else if(data<5650) { result="5650";
                } else if(data<5750) { result="5750";
                } else if(data<5850) { result="5850";
                } else if(data<5950) { result="5950";
                } else if(data<6050) { result="6050";
                } else if(data<6150) { result="6150";
                } else if(data<6250) { result="6250";
                } else if(data<6350) { result="6350";
                } else if(data<6450) { result="6450";
                } else if(data<6550) { result="6550";
                } else if(data<6650) { result="6650";
                } else if(data<6750) { result="6750";
                } else if(data<6850) { result="6850";
                } else if(data<6950) { result="6950";
                } else if(data<7050) { result="7050";
                } else if(data<7150) { result="7150";
                } else if(data<7250) { result="7250";
                } else if(data<7350) { result="7350";
                } else if(data<7450) { result="7450";
                } else if(data<7550) { result="7550";
                } else if(data<7650) { result="7650";
                } else if(data<7750) { result="7750";
                } else if(data<7850) { result="7850";
                } else if(data<7950) { result="7950";
                } else if(data<8050) { result="8050";
                } else if(data<8150) { result="8150";
                } else if(data<8250) { result="8250";
                } else if(data<8350) { result="8350";
                } else if(data<8450) { result="8450";
                } else if(data<8550) { result="8550";
                } else if(data<8650) { result="8650";
                } else if(data<8750) { result="8750";
                } else if(data<8850) { result="8850";
                } else if(data<8950) { result="8950";
                } else if(data<9050) { result="9050";
                } else if(data<9150) { result="9150";
                } else if(data<9250) { result="9250";
                } else if(data<9350) { result="9350";
                } else if(data<9450) { result="9450";
                } else if(data<9550) { result="9550";
                } else if(data<9650) { result="9650";
                } else if(data<9750) { result="9750";
                } else if(data<9850) { result="9850";
                } else if(data<9950) { result="9950";
                } else { result="over";
                }
                resultProcess(result);
            }
    		
        }
        String current;
    	
        public void resultProcess(String result) {
            current=result;
        }
    	
    }
    ここでrandomOnly()メソッドは、ランダムな数値を作成し、resultProcess()メソッドを呼び出す操作を実行します.randomOnly()メソッドを作成するには、ifの存在を比較するための基準が必要です.
    JMHでif文の性能を測定した結果を見てみましょう.
    オブジェクト
    応答時間(マイクロ秒)
    randomOnly
    0.46
    10個なら
    5
    100個
    63
    その結果,ifゲートを10個通過すると,なかった時より10倍長い時間が必要となる.また、100個だと140倍以上の時間がかかります.ここに必要な時間は、非常に小さな数字である場合もあれば、大きな数字である場合もあります.
    しかし、ここには少し注意が必要です.この例のコードは、if文が10個あってもLOOP COUNTの繰り返し回数が1000であるため、if文が合計10000回経過した結果がif 10()の値であることを示す.したがって、ifが1つしかない場合、既存のコードよりもパフォーマンスが約「応答時間/1000」高いとは考えにくい.
    switch文も同様に迅速な応答結果を生成します.Oracleサイトのドキュメントでは、switchはifよりも数値の比較に毒性があるため、固定数値に分割する場合はswitchを使用することを推奨します.
    さらに、JDK 7では、String文字列をswitch文に使用することができる.英語の月を次のように数字に変更する方法があります.
    package com.perf.condition;
    
    public class SwitchCaseString {
    
        public static void main(String[] args) {
            SwitchCaseString scs=new SwitchCaseString();
            scs.getMonthNumber("February");
        }
        public int getMonthNumber(String str) {
            int month=-1;
            switch(str) {
            case "January": month=1;
                break;
            case "February": month=2;
                break;
            case "March" : month=3;
                break;
            case "April": month=4;
                break;
            case "May": month=5;
                break;
            case "June": month=6;
                break;
            case "July": month=7;
                break;
            case "August": month=8;
                break;
            case "September": month=9;
                break;
            case "October": month=10;
                break;
            case "November": month=11;
                break;
            case "December": month=12;
                break;
            }
    //	System.out.println("January".hashCode());
    //	System.out.println("February".hashCode());
    //	System.out.println("March".hashCode());
    //	System.out.println("April".hashCode());
    
            return month;
        }
    	
    }
    JDK 6まで、switch-case文は主に整数とenumを処理して、どのようにJDK 7の中でStringを比較しますか?答えはint整数を返すObjectクラスで宣言されるhashCode()というメソッドです.StringのOverridingのhashCode()メソッドは、文字列をint値に分割し、switch-case文で使用します.すなわち,コンパイル中にcase文の各値をhashCodeに変換し,値の小さいものから並べ替え,Stringのequals()メソッドを用いて実際の値と同じかどうかを比較する.したがってStringは利用可能です.
    しかし、ここに覚えなければならないことがある.つまり、これらの数字は揃えられています.switch-case文は小数から比較的大きい数が最も速い.ターゲットcaseの数が少ないほど良いが、caseの数が多いほど時間がかかる.従って,単純なswitch−caseでも性能を考慮する.
    文で条件が満たされている場合、カッコ内で何も実行されない場合、JavaのJITコンパイラは、最適化によってコードを無視する可能性があります。

    2.繰り返し文法の速度は?


    JAvaで使用される重複構文は3種類あります.
  • for
  • do-while
  • while
  • 一般的にfor文を多く使用します.while文も使用する場合がありますが、while文が間違っていると無限ループに陥る可能性があるので、できるだけfor文を使用することをお勧めします.
    public void test(ArrayList<String> list) {
        boolean flag = true;
        int idx = 0;
        do {
            if(list.get(idx).equals("A")) flag=false;
        } while(flag);
    }
    ArrayListの最初の値が「A」の場合、アプリケーションは正常に動作します.そうしないと、サーバを再起動したり、スレッドを強制的に閉じたりするまで、アプリケーションは繰り返し文を実行し続けます.そうなると、当然サーバにも多くの負荷がかかります.
    では、for文法について詳しく説明しましょう.JDK 5.0の前にfor構文を使用すると、次のようになります.リストは値付きArrayListです.
    for(int loop=0; loop<list.size(); loop++)
    このようにコードする習慣はよくありません.リストを繰り返すたびに.size()という名前のメソッドを呼び出すからです.次のように変更します.
    int listSize = list.size();
    for(int loop=0; loop<listSize; loop++)
    これにより、不要なsize()メソッドの繰り返し呼び出しが解消され、処理速度が速くなります.
    5.0から、For-EAchと呼ばれるforループを使用できます.
    ArrayList<String> list = new ArrayList<String>();
    '''
    for(String str : list)
    For-EAchでは、フォーマットを個別に変換したりget()メソッドやelementAt()メソッドを呼び出すことなく、for文でStringオブジェクトを順番に使用できますので、便利です.しかし、この方法は、データの最初の再値を最後の値に処理する必要がある場合にのみ有用である.順序を反転したり、特定の値からデータのブラウズを開始したりすると、適切ではありません.
    では、これまでに出てきた方法の性能を比較してみましょう.
    package com.perf.condition;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    import org.openjdk.jmh.annotations.BenchmarkMode;
    import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
    import org.openjdk.jmh.annotations.Mode;
    import org.openjdk.jmh.annotations.OutputTimeUnit;
    import org.openjdk.jmh.annotations.Scope;
    import org.openjdk.jmh.annotations.Setup;
    import org.openjdk.jmh.annotations.State;
    
    @State(Scope.Thread)
    @BenchmarkMode({ Mode.AverageTime })
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public class ForLoop {
    	
        int LOOP_COUNT=100000;
        List<Integer> list;
    	
        @Setup
        public void setUp() {
            list=new ArrayList<Integer>(LOOP_COUNT);
            for(int loop=0;loop<LOOP_COUNT;loop++) {
                list.add(loop);
            }
        }
        @GenerateMicroBenchmark
        public void traditionalForLoop() {
            int listSize=list.size();
            for(int loop=0;loop<listSize;loop++) {
                resultProcess(list.get(loop));
            }
        }
        @GenerateMicroBenchmark
        public void traditionalSizeForLoop() {
            for(int loop=0;loop<list.size();loop++) {
                resultProcess(list.get(loop));
            }
        }
        @GenerateMicroBenchmark
        public void timeForEachLoop() {
            for(Integer loop:list) {
                resultProcess(loop);
            }
        }
        int current;
        public void resultProcess(int result) {
            current=result;
        }
    	
    }
    結果はどうなりますか?理論的にはlistを繰り返すたびに.size()メソッドの呼び出し部分は最も遅く、JDK 5.0に追加されたfor文は最も速くなければなりません.
    JMHで測定した結果は以下の通りであった.
    オブジェクト
    応答時間(マイクロ秒)
    for
    410
    サイズの比較を繰り返す
    413
    for-each
    481
    これまで最も速くて便利な方法は,配列やArrayListの大きさを先に読み出し,繰返し構文を回転させることであった.もちろん、これは例では10万回繰り返したために違いますが、実際に稼働しているWebシステムでは、これほど繰り返されることはないので、差はないはずです.ただし、検証セクションでサイズの比較を続行する構文は避けてください.

    3.重複構築で不要な重複


    最大のエラーの1つは、重複構文で不要なメソッド呼び出しを続行することです.
    public void sample(DataVO data, String key) {
        TreeSet treeSet2 = null;
        treeSet2 = (TreeSet)data.get(key);
        if(treeSet != null) {
            for(int i = 0; i < treeSet2.size(); i++) {
                DataVO data2 = (DataVO)treeSet2.toArray()[i];
                ...
            }
        }
    }
    TreeSet形式のデータを持つDataVOでTreeSetを呼び出して処理する.このソースの問題はtoArray()メソッドを繰り返し実行することである.
    アプリケーションを一度に呼び出すと、sampleメソッドは40回実行されます.また、TreeSet 2オブジェクトには256個のデータが含まれているため、toArray()メソッドを呼び出すたびに10600回繰り返し呼び出される.したがって、toArray()メソッドの重複を回避するために、このコードをfor文の前に移動することが望ましい.さらにこのソースコードのfor文を見るとtreeSet 2.size()メソッドを継続的に呼び出す必要があります.修正の結果は以下の通りです.
    public void sample(DataVO data, String key) {
        TreeSet treeSet2 = null;
        treeSet2 = (TreeSet)data.get(key);
        if(treeSet2 != null) {
            DataVO2[] dataVO2 = (DataVO2)treeSet2.toArray();
            int treeSet2Size = treeSet2.size();
            for(int i = 0; i < treeSet2Size; i++) {
                DataVO data2 = dataVO2[i];
            }
        }
    }
    リファレンス
  • Javaパフォーマンスチューニングストーリー