001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with this
004 * work for additional information regarding copyright ownership. The ASF
005 * licenses this file to you under the Apache License, Version 2.0 (the
006 * "License"); you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 * License for the specific language governing permissions and limitations under
015 * the License.
016 */
017package org.apache.hadoop.hbase.io.hfile;
018
019/**
020 * A nano-second timer.
021 * <p>
022 * Copied from
023 * <a href="https://issues.apache.org/jira/browse/HADOOP-3315">hadoop-3315 tfile</a>.
024 * Remove after tfile is committed and use the tfile version of this class
025 * instead.</p>
026 */
027public class NanoTimer {
028  private long last = -1;
029  private boolean started = false;
030  private long cumulate = 0;
031
032  /**
033   * Constructor
034   *
035   * @param start
036   *          Start the timer upon construction.
037   */
038  public NanoTimer(boolean start) {
039    if (start) this.start();
040  }
041
042  /**
043   * Start the timer.
044   *
045   * Note: No effect if timer is already started.
046   */
047  public void start() {
048    if (!this.started) {
049      this.last = System.nanoTime();
050      this.started = true;
051    }
052  }
053
054  /**
055   * Stop the timer.
056   *
057   * Note: No effect if timer is already stopped.
058   */
059  public void stop() {
060    if (this.started) {
061      this.started = false;
062      this.cumulate += System.nanoTime() - this.last;
063    }
064  }
065
066  /**
067   * Read the timer.
068   *
069   * @return the elapsed time in nano-seconds. Note: If the timer is never
070   *         started before, -1 is returned.
071   */
072  public long read() {
073    if (!readable()) return -1;
074
075    return this.cumulate;
076  }
077
078  /**
079   * Reset the timer.
080   */
081  public void reset() {
082    this.last = -1;
083    this.started = false;
084    this.cumulate = 0;
085  }
086
087  /**
088   * Checking whether the timer is started
089   *
090   * @return true if timer is started.
091   */
092  public boolean isStarted() {
093    return this.started;
094  }
095
096  /**
097   * Format the elapsed time to a human understandable string.
098   *
099   * Note: If timer is never started, "ERR" will be returned.
100   */
101  @Override
102  public String toString() {
103    if (!readable()) {
104      return "ERR";
105    }
106
107    return NanoTimer.nanoTimeToString(this.cumulate);
108  }
109
110  /**
111   * A utility method to format a time duration in nano seconds into a human
112   * understandable stirng.
113   *
114   * @param t
115   *          Time duration in nano seconds.
116   * @return String representation.
117   */
118  public static String nanoTimeToString(long t) {
119    if (t < 0) return "ERR";
120
121    if (t == 0) return "0";
122
123    if (t < 1000) {
124      return t + "ns";
125    }
126
127    double us = (double) t / 1000;
128    if (us < 1000) {
129      return String.format("%.2fus", us);
130    }
131
132    double ms = us / 1000;
133    if (ms < 1000) {
134      return String.format("%.2fms", ms);
135    }
136
137    double ss = ms / 1000;
138    if (ss < 1000) {
139      return String.format("%.2fs", ss);
140    }
141
142    long mm = (long) ss / 60;
143    ss -= mm * 60;
144    long hh = mm / 60;
145    mm -= hh * 60;
146    long dd = hh / 24;
147    hh -= dd * 24;
148
149    if (dd > 0) {
150      return String.format("%dd%dh", dd, hh);
151    }
152
153    if (hh > 0) {
154      return String.format("%dh%dm", hh, mm);
155    }
156
157    if (mm > 0) {
158      return String.format("%dm%.1fs", mm, ss);
159    }
160
161    return String.format("%.2fs", ss);
162
163    /**
164     * StringBuilder sb = new StringBuilder(); String sep = "";
165     *
166     * if (dd > 0) { String unit = (dd > 1) ? "days" : "day";
167     * sb.append(String.format("%s%d%s", sep, dd, unit)); sep = " "; }
168     *
169     * if (hh > 0) { String unit = (hh > 1) ? "hrs" : "hr";
170     * sb.append(String.format("%s%d%s", sep, hh, unit)); sep = " "; }
171     *
172     * if (mm > 0) { String unit = (mm > 1) ? "mins" : "min";
173     * sb.append(String.format("%s%d%s", sep, mm, unit)); sep = " "; }
174     *
175     * if (ss > 0) { String unit = (ss > 1) ? "secs" : "sec";
176     * sb.append(String.format("%s%.3f%s", sep, ss, unit)); sep = " "; }
177     *
178     * return sb.toString();
179     */
180  }
181
182  private boolean readable() {
183    return this.last != -1;
184  }
185
186  /**
187   * Simple tester.
188   *
189   * @param args
190   */
191  public static void main(String[] args) {
192    long i = 7;
193
194    for (int x = 0; x < 20; ++x, i *= 7) {
195      System.out.println(NanoTimer.nanoTimeToString(i));
196    }
197  }
198}
199