ストップウォッチ

/**
 * 時間計測用クラス
 */
public class StopWatch {

  static {
    // JITコンパイルでも走るのか、最初の1回目は遅いので適当に動かしておく
    StopWatch s = new StopWatch(2, TimeUnit.NANOSECONDS);
    s.start();
    s.phase("");
    s.split("");
    s.stop();
    s.phases();
  }

  /**
   * 個々の経過時間を保持するクラス
   */
  public class Phase {

    private String _name;
    private AtomicLong _start;
    private long _end;

    /**
     * 記録を識別する名前を返す
     *
     * @return 名前
     */
    public String getName() {
      return _name;
    }

    /**
     * 経過時間を返す。<br/>
     * 単位はStopWatchクラスのコンストラクタに指定した{@link TimeUnit}による。(デフォルトはナノ秒)
     *
     * @return 時間
     */
    public long getTime() {
      return _timeUnit.convert(_end - _start.get(), TimeUnit.NANOSECONDS);
    }

    /**
     * "名前=時間"形式の文字列を返す
     *
     * @return
     */
    @Override
    public String toString() {
      return String.format("%s=%d", _name, getTime());
    }

  }

  private int _phaseCount = 1; // stopの分
  private TimeUnit _timeUnit = TimeUnit.NANOSECONDS;
  private long _startTime = -1;
  private Phase[] _phases;
  private AtomicInteger _phaseIndex = new AtomicInteger();

  /**
   * デフォルトコンストラクタ。<br/>
   * {@link #phase(String)}、{@link #split(String)}は使用できない。
   */
  public StopWatch() {}

  /**
   * フェーズ計測、スプリット計測をするためのコンストラクタ
   *
   * @param phaseCount
   *            想定される{@link #phase(String)}、{@link #split(String)}の最大呼出し回数。<br/>
   *            この数を超えて呼出すとエラーになる。
   * @param timeUnit
   *            測定結果の単位。内部的にはすべてナノ秒で記録するが、各メソッドの結果はこの単位で丸められる。
   */
  public StopWatch(int phaseCount, TimeUnit timeUnit) {
    _phaseCount = phaseCount + 1;
    _timeUnit = timeUnit;
  }

  /**
   * 計測を開始する
   */
  public void start() {
    _phases = new Phase[_phaseCount];
    // phase()呼出し時に後続のすべてのPhaseの開始時間を再設定していると時間がかかるので、それを1回で済ませるためにこの時点ではすべてのPhaseで開始時間を共有する
    AtomicLong start = new AtomicLong();
    for (int i = 0; i < _phaseCount; i++) {
      _phases[i] = new Phase();
      _phases[i]._start = start;
    }
    _phaseIndex.set(0);
    // 開始時間の取得は極力遅らせる
    start.set(_startTime = System.nanoTime());
  }

  /**
   * {@link #start()}もしくは直前の{@link #phase(String)}呼出しからの経過時間を記録する。<br/>
   * このメソッドのみスレッドセーフ。
   *
   * @param name
   *            記録を識別する名前
   * @return 経過時間
   */
  public long split(String name) {
    long now = System.nanoTime();
    checkRunning();
    Phase phase = _phases[_phaseIndex.getAndIncrement()];
    phase._name = name;
    // 後続のPhaseと開始時間を切り離すために再設定
    phase._start = new AtomicLong(phase._start.get());
    phase._end = now;
    return phase.getTime();
  }

  /**
   * {@link #start()}もしくは直前の{@link #phase(String)}呼出しからの経過時間を記録する
   *
   * @param name
   *            記録を識別する名前
   * @return 経過時間
   */
  public long phase(String name) {
    long now = System.nanoTime();
    checkRunning();
    Phase phase = _phases[_phaseIndex.getAndIncrement()];
    phase._name = name;
    // 後続のPhaseと開始時間を切り離すために再設定
    phase._start = new AtomicLong(phase._start.get());
    phase._end = now;
    long ret = phase.getTime();
    // 後続のPhaseの開始時間を再設定
    // 開始時間の取得は極力遅らせる
    // 後続のPhaseは同じAtomicLongを共有しているので再設定するのは1つだけでOK
    _phases[_phaseIndex.get()]._start.set(System.nanoTime());
    return ret;
  }

  /**
   * 計測を止める
   *
   * @return {@link #start()}からの経過時間
   */
  public long stop() {
    long now = System.nanoTime();
    checkRunning();
    Phase phase = _phases[_phaseIndex.getAndIncrement()];
    phase._name = "stop";
    phase._start = new AtomicLong(_startTime);
    phase._end = now;
    return phase.getTime();
  }

  private void checkRunning() {
    if (_startTime == -1) {
      throw new IllegalStateException();
    }
  }

  /**
   * 記録した結果を配列で返す
   *
   * @return
   */
  public Phase[] phases() {
    return Arrays.copyOf(_phases, _phaseIndex.get());
  }

}