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