2010年1月12日火曜日

TPTP/BigDecimal

以前から、BigDecimal を使うとき MathContext をどう選ぶか気になる事がたまにあった。

割り算をするときに、DECIMAL32、DECIMAL64、DECIMAL128のうちのどれかを指定するけど、時間とかメモリ使用量とかどんなトレードオフがあるんだろう。

とりあえずざっと時間を計ってみた。(というか計り方を確認してみたかった。)

以下のようなコードで、1 を 3 で割って 3 を掛けなおす計算を、各 MathContext で 100万回ずつ繰り返すメソッドを呼んでみる。
public class Main {
  private static final BigDecimal three = new BigDecimal(3);
  public static void main(String[] args) {
    repeat(1000000, MathContext.DECIMAL32);
    repeat(1000000, MathContext.DECIMAL64);
    repeat(1000000, MathContext.DECIMAL128);
  }
  private static void repeat(int count, MathContext mc) {
    BigDecimal result = new BigDecimal(1);
    for (int i = 0; i < count; ++i) {
      result = result.divide(three, mc).multiply(three);
    }
    System.out.println(result);
  }
}
この Main クラスを[Profile As]/[Java Application] で実行すると、以下のような結果を出力する裏で、プロファイル情報が収集される。(ここでは「時間」を指定した)
0.9999999
0.9999999999999999
0.9999999999999999999999999999999999
Profiling and Logging パースペクティブの Profiling Monitor ビューから、収集したプロファイル情報を開くと、Execution Statistics ビューがエディタペインに開く。

このビューのCall Tree タブを選択すると、下図のような表示が得られる。
画像下部の[Previous], [Next] ボタンで3つの呼び出しを選択でき、実行時間に占めるパーセンテージが表示される。まとめると以下のようになる。
DECIMAL32約10%
DECIMAL64約24%
DECIMAL128約64%
上の計算パターンのみだと一概には言えないけど、DECIMAL128 でもそうべらぼうに遅くはならないらしい。DECIMAL32 とくらべて 6倍ちょっと(2.5 の2乗?)だから、計算コードを書くときは、まず 一番わかりやすいアルゴリズムで DECIMAL128 を使ってコーディングして、速くする必要が生じたときに、ちゃんと測定してボトルネックを見つけてから、必要なら別の MathContext を考えるという流れでよさそう。

それと計算時間に加えてメモリ使用量についても傾向をつかんでおけば、まあまあ根拠のある設計判断ができそう。後は仕様と相談か。

0 件のコメント:

コメントを投稿