2 * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.yangtools.util;
11 import static java.util.concurrent.TimeUnit.MICROSECONDS;
12 import static java.util.concurrent.TimeUnit.MILLISECONDS;
13 import static java.util.concurrent.TimeUnit.NANOSECONDS;
14 import static java.util.concurrent.TimeUnit.SECONDS;
15 import java.text.DecimalFormat;
16 import java.text.DecimalFormatSymbols;
17 import java.util.Date;
18 import java.util.concurrent.TimeUnit;
19 import java.util.concurrent.atomic.AtomicLongFieldUpdater;
20 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
25 * Class that calculates and tracks time duration statistics.
27 * @author Thomas Pantelis
28 * @author Robert Varga
30 public class DurationStatsTracker {
31 private static final AtomicReferenceFieldUpdater<DurationStatsTracker, DurationWithTime> LONGEST_UPDATER =
32 AtomicReferenceFieldUpdater.newUpdater(DurationStatsTracker.class, DurationWithTime.class, "longest");
33 private static final AtomicReferenceFieldUpdater<DurationStatsTracker, DurationWithTime> SHORTEST_UPDATER =
34 AtomicReferenceFieldUpdater.newUpdater(DurationStatsTracker.class, DurationWithTime.class, "shortest");
35 private static final AtomicLongFieldUpdater<DurationStatsTracker> COUNT_UPDATER =
36 AtomicLongFieldUpdater.newUpdater(DurationStatsTracker.class, "count");
37 private static final AtomicLongFieldUpdater<DurationStatsTracker> SUM_UPDATER =
38 AtomicLongFieldUpdater.newUpdater(DurationStatsTracker.class, "sum");
40 private static final Logger LOG = LoggerFactory.getLogger(DurationStatsTracker.class);
41 private static final DecimalFormat DECIMAL_FORMAT;
43 private volatile long sum = 0;
44 private volatile long count = 0;
45 private volatile DurationWithTime longest = null;
46 private volatile DurationWithTime shortest = null;
49 final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
50 symbols.setDecimalSeparator('.');
51 DECIMAL_FORMAT = new DecimalFormat("0.00", symbols);
55 * Add a duration to track.
58 * the duration in nanoseconds.
60 public void addDuration(final long duration) {
61 // First update the quick stats
62 SUM_UPDATER.addAndGet(this, duration);
63 COUNT_UPDATER.incrementAndGet(this);
66 * Now the hairy 'min/max' things. The notion of "now" we cache,
67 * so the first time we use it, we do not call it twice. We populate
70 * The longest/shortest stats both are encapsulated in an object,
71 * so we update them atomically and we minimize the number of volatile
74 DurationWithTime current = shortest;
75 if (current == null || current.getDuration() > duration) {
76 final DurationWithTime newObj = new DurationWithTime(duration, System.currentTimeMillis());
77 while (!SHORTEST_UPDATER.compareAndSet(this, current, newObj)) {
79 if (current != null && current.getDuration() <= duration) {
86 if (current == null || current.getDuration() < duration) {
87 final DurationWithTime newObj = new DurationWithTime(duration, System.currentTimeMillis());
88 while (!LONGEST_UPDATER.compareAndSet(this, current, newObj)) {
90 if (current != null && current.getDuration() >= duration) {
98 * Returns the total number of tracked durations.
100 public long getTotalDurations() {
104 private static long getDuration(final DurationWithTime current) {
105 return current == null ? 0L : current.getDuration();
108 private static long getTimeMillis(final DurationWithTime current) {
109 return current == null ? 0L : current.getTimeMillis();
113 * Returns the longest duration in nanoseconds.
115 public long getLongestDuration() {
116 return getDuration(longest);
120 * Returns the shortest duration in nanoseconds.
122 public long getShortestDuration() {
123 return getDuration(shortest);
127 * Returns the average duration in nanoseconds.
129 public double getAverageDuration() {
130 final long mySum = sum;
131 final long myCount = count;
133 return myCount == 0 ? 0 : ((double) mySum) / myCount;
137 * Returns the time stamp of the longest duration.
139 public long getTimeOfLongestDuration() {
140 return getTimeMillis(longest);
144 * Returns the time stamp of the shortest duration.
146 public long getTimeOfShortestDuration() {
147 return getTimeMillis(shortest);
151 * Resets all statistics back to their defaults.
153 public synchronized void reset() {
161 * Returns the average duration as a displayable String with units, e.g.
164 public String getDisplayableAverageDuration() {
165 return formatDuration(getAverageDuration(), null);
169 * Returns the shortest duration as a displayable String with units and the
170 * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
172 public String getDisplayableShortestDuration() {
173 return formatDuration(shortest);
177 * Returns the longest duration as a displayable String with units and the
178 * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
180 public String getDisplayableLongestDuration() {
181 return formatDuration(longest);
185 * Returns formatted value of number, e.g. "12.34". Always is used dot as
188 private static synchronized String formatDecimalValue(final double value) {
189 return DECIMAL_FORMAT.format(value);
192 private static String formatDuration(final DurationWithTime current) {
193 if (current != null) {
194 return formatDuration(current.getDuration(), current.getTimeMillis());
196 return formatDuration(0, null);
200 private static String formatDuration(final double duration, final Long timeStamp) {
201 final TimeUnit unit = chooseUnit((long) duration);
202 final double value = duration / NANOSECONDS.convert(1, unit);
204 final StringBuilder sb = new StringBuilder();
205 sb.append(formatDecimalValue(value));
207 sb.append(abbreviate(unit));
209 if (timeStamp != null) {
210 sb.append(String.format(" at %1$tD %1$tT", new Date(timeStamp)));
213 return sb.toString();
216 private static TimeUnit chooseUnit(final long nanos) {
217 // TODO: this could be inlined, as we are doing needless divisions
218 if (NANOSECONDS.toSeconds(nanos) > 0) {
221 if (NANOSECONDS.toMillis(nanos) > 0) {
224 if (NANOSECONDS.toMicros(nanos) > 0) {
230 private static String abbreviate(final TimeUnit unit) {
235 return "\u03bcs"; // μs
248 LOG.warn("Unhandled time unit {}", unit);