例外処理は処理コストが掛かるらしいことを確かめようとしたら、意外とそうでもなかった話

例外処理はパフォーマンスが低下するという話がよくあるので、確かめてみた。メソッド処理でエラーが起こった場合、返却値としてステータスコードを返すパターンと、例外を投げるパターンで処理時間を計測した。

で、下で示したコードで検証したところ、エラー発生時の処理コストはステータスコードを返すパターンの方が処理時間は少なかったけど、エラーが発生しない場合は例外処理を使うパターンの方が処理時間は少なかったという結果となった。

この検証では、パフォーマンス上の理由から例外処理機構を積極的に使うべきではないというのは根拠が薄いという結論です。厳密には期待されるエラー処理回数と正常処理回数の割合から採用を選ぶべきと言えるかもしれない(パラメータいじったら、エラー率が2割以下なら例外処理が勝った)けど、エラー処理っていうのは通常の処理よりも実行される回数は少ないのが普通なわけで、例外処理大好きっ子としては「例外処理を使いましょう」と推薦させて頂きます。

検証結果から

case1が例外処理を使わず、返却値で返した場合。case2は例外処理を使った場合。

エラー率:99.9997%

case1:171ms
case2:1922ms

例外処理を使うパターンで、10倍近いコストが掛かっている様子。

エラー率:20%

case1:999ms
case2:954ms

大体同じくらい。

エラー率:0%

case1:1031ms
case2:578ms

例外処理を使うパターンの方が、完全に速い。

検証コード

  • Zooインスタンスにはanimalをキープできるが、数に限度がある。限度を超えてキープしようとした場合、エラーとして認識できるようにする。
    • エラー時、keep1では返却値に-1を返す
    • エラー時、keep2ではNoMoreRoomsExceptionを投げる
  • case1、case2ともにエラー時の判定のみ行い、エラー処理は行わない。エラー処理に特化した処理時間を計測。
  • エラー率を変えた測定は、Zoo.limitを手でイジッて実行してみましょう(笑)
import java.util.*;

public class Sample {
    public static void main(String[] args) {
        {
            long s = System.currentTimeMillis();
            case1();
            System.out.println("case1:"+(System.currentTimeMillis()-s)+"ms");
        }
        
        {
            long s = System.currentTimeMillis();
            case2();
            System.out.println("case2:"+(System.currentTimeMillis()-s)+"ms");
        }
    }
    
    public static void case1() {
        Zoo hatenaZoo = new Zoo();
        for (int i = 0; i < 1000000; i++) {
            if (hatenaZoo.keep1(String.valueOf(i)) != 0);
        }
    }
    
    public static void case2() {
        Zoo hatenaZoo = new Zoo();
        for (int i = 0; i < 1000000; i++) {
            try {
                hatenaZoo.keep2(String.valueOf(i));
            } catch (Zoo.NoMoreRoomsException e) {
            }
        }
        
    }
}

class Zoo {
    Collection<String> animalCollection;
    final int limit = 3;
    
    public Zoo() {animalCollection = new ArrayList<String>(1000000);}
    
    public String toString() {return animalCollection.toString();}
    
    private boolean hasVacantRoom() {return animalCollection.size() < limit;}
    
    public int keep1(String animal) {
        if (!hasVacantRoom()) return -1;
        animalCollection.add(animal);
        return 0;
    }
    
    public void keep2(String animal) throws NoMoreRoomsException {
        if (!hasVacantRoom()) throw new NoMoreRoomsException();
        animalCollection.add(animal);
    }
    
    public class NoMoreRoomsException extends Exception {}
}