This code accompanies the article Evaluating Simon.
Quick download link:
StopwatchImpl -- improved --.java
package org.javasimon;
import org.javasimon.utils.SimonUtils;
import java.util.*;
/**
* Class implements {@link org.javasimon.Stopwatch} interface - see there for how to use Stopwatch.
*
* @see org.javasimon.Stopwatch
*
* @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a>
* @created Aug 4, 2008
*/
final class StopwatchImpl extends AbstractSimon implements Stopwatch {
private long total;
private long counter;
private long active;
private long max;
private long maxTimestamp;
private long maxActive;
private long maxActiveTimestamp;
private long min = Long.MAX_VALUE;
private long minTimestamp;
private long last;
private ThreadLocal<Long> threadBasedSplitMap = new ThreadLocal<Long>();
private Map<Object, Long> splitMap = new HashMap<Object, Long>();
private long firstUsageNanos;
private long currentNanos;
StopwatchImpl(String name) {
super(name);
}
/**
* {@inheritDoc}
*/
public synchronized Stopwatch addTime(long ns) {
if (enabled) {
updateUsages();
addSplit(ns);
}
return this;
}
/**
* {@inheritDoc}
*/
public Stopwatch start() {
if (enabled) {
// if (threadBasedSplitMap.get() != null) {
// throw new SimonException("Illegal start - there is another split running in the current thread: " + Thread.currentThread().getId());
// }
// updateUsages();
// activeStart();
// threadBasedSplitMap.set(currentNanos);
return new StopwatchMeasurement(System.nanoTime());
}
return this;
}
/**
* {@inheritDoc}
*/
public synchronized long stop() {
if (enabled) {
throw new UnsupportedOperationException();
// Long start = threadBasedSplitMap.get();
// if (start == null) {
// return 0;
// }
// active--;
// threadBasedSplitMap.remove();
// updateUsages();
// return addSplit(currentNanos - start);
}
return 0;
}
/**
* {@inheritDoc}
*/
public synchronized Stopwatch start(Object key) {
if (enabled) {
if (splitMap.containsKey(key)) {
throw new SimonException("Illegal start - there is another split running for the specified key: " + key);
}
updateUsages();
activeStart();
splitMap.put(key, currentNanos);
}
return this;
}
/**
* {@inheritDoc}
*/
public synchronized long stop(Object key) {
if (enabled) {
Long start = splitMap.remove(key);
if (start == null) {
return 0;
}
active--;
updateUsages();
return addSplit(currentNanos - start);
}
return 0;
}
// Uses last usage, hence it must be placed after usages update
private void activeStart() {
active++;
if (active >= maxActive) {
maxActive = active;
maxActiveTimestamp = getLastUsage();
}
}
/**
* {@inheritDoc}
*/
public synchronized Stopwatch reset() {
total = 0;
counter = 0;
max = 0;
min = Long.MAX_VALUE;
maxTimestamp = 0;
minTimestamp = 0;
// active is not reset, because
maxActive = 0;
maxActiveTimestamp = 0;
getStatProcessor().reset();
return this;
}
private long addSplit(long split) {
last = split;
total += split;
counter++;
if (split > max) {
max = split;
maxTimestamp = getLastUsage();
}
if (split < min) {
min = split;
minTimestamp = getLastUsage();
}
if (getStatProcessor() != null) {
getStatProcessor().process(split);
}
return split;
}
/**
* {@inheritDoc}
*/
public synchronized long getTotal() {
return total;
}
/**
* {@inheritDoc}
*/
public long getLast() {
return last;
}
/**
* {@inheritDoc}
*/
public synchronized long getCounter() {
return counter;
}
/**
* {@inheritDoc}
*/
public synchronized long getMax() {
return max;
}
/**
* {@inheritDoc}
*/
public synchronized long getMin() {
return min;
}
/**
* {@inheritDoc}
*/
public synchronized long getMaxTimestamp() {
return maxTimestamp;
}
/**
* {@inheritDoc}
*/
public synchronized long getMinTimestamp() {
return minTimestamp;
}
/**
* {@inheritDoc}
*/
public long getActive() {
return active;
}
/**
* {@inheritDoc}
*/
public long getMaxActive() {
return maxActive;
}
/**
* {@inheritDoc}
*/
public long getMaxActiveTimestamp() {
return maxActiveTimestamp;
}
@Override
protected void disabledObserver() {
active = 0;
threadBasedSplitMap = new ThreadLocal<Long>();
splitMap.clear();
}
@Override
public void setStatProcessor(StatProcessor statProcessor) {
super.setStatProcessor(statProcessor);
statProcessor.setInterpreter(StatProcessor.NanoInterpreter.INSTANCE);
}
/**
* {@inheritDoc}
*/
public synchronized Map<String, String> sample(boolean reset) {
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("total", String.valueOf(total));
map.put("counter", String.valueOf(counter));
map.put("min", String.valueOf(min));
map.put("max", String.valueOf(max));
map.put("minTimestamp", String.valueOf(minTimestamp));
map.put("maxTimestamp", String.valueOf(maxTimestamp));
map.putAll(getStatProcessor().sample(false)); // reset is done via Simon's reset method
if (reset) {
reset();
}
return map;
}
/**
* Updates usage statistics.
*/
protected void updateUsages() {
currentNanos = System.nanoTime();
if (firstUsage == 0) {
firstUsage = System.currentTimeMillis();
firstUsageNanos = currentNanos;
}
lastUsage = firstUsage + (currentNanos - firstUsageNanos) / SimonUtils.NANOS_IN_MILLIS;
}
@Override
public String toString() {
return "Simon Stopwatch: " + super.toString() +
" total " + SimonUtils.presentNanoTime(total) +
", counter " + counter +
", max " + SimonUtils.presentNanoTime(max) +
", min " + SimonUtils.presentNanoTime(min) +
(getNote() != null && getNote().length() != 0 ? ", note '" + getNote() + "'" : "");
}
private class StopwatchMeasurement implements Stopwatch {
private long startNanos;
public StopwatchMeasurement(long startNanos) {
this.startNanos = startNanos;
}
public long stop() {
long split = System.nanoTime() - startNanos;
synchronized (StopwatchImpl.this) {
return addSplit(split);
}
}
public Stopwatch addTime(long ns) {
throw new UnsupportedOperationException();
}
public Stopwatch start() {
throw new UnsupportedOperationException();
}
public Stopwatch start(Object key) {
throw new UnsupportedOperationException();
}
public long stop(Object key) {
throw new UnsupportedOperationException();
}
public long getTotal() {
throw new UnsupportedOperationException();
}
public long getLast() {
throw new UnsupportedOperationException();
}
public long getCounter() {
throw new UnsupportedOperationException();
}
public long getMax() {
throw new UnsupportedOperationException();
}
public long getMin() {
throw new UnsupportedOperationException();
}
public long getMaxTimestamp() {
throw new UnsupportedOperationException();
}
public long getMinTimestamp() {
throw new UnsupportedOperationException();
}
public Simon getParent() {
throw new UnsupportedOperationException();
}
public Collection<Simon> getChildren() {
throw new UnsupportedOperationException();
}
public String getName() {
throw new UnsupportedOperationException();
}
public SimonState getState() {
throw new UnsupportedOperationException();
}
public void setState(SimonState state, boolean overrule) {
throw new UnsupportedOperationException();
}
public boolean isEnabled() {
throw new UnsupportedOperationException();
}
public Stopwatch reset() {
throw new UnsupportedOperationException();
}
public StatProcessor getStatProcessor() {
throw new UnsupportedOperationException();
}
public void setStatProcessor(StatProcessor statProcessor) {
throw new UnsupportedOperationException();
}
public String getNote() {
throw new UnsupportedOperationException();
}
public void setNote(String note) {
throw new UnsupportedOperationException();
}
public long getFirstUsage() {
throw new UnsupportedOperationException();
}
public long getLastUsage() {
throw new UnsupportedOperationException();
}
public Map<String, String> sample(boolean reset) {
throw new UnsupportedOperationException();
}
public long getActive() {
throw new UnsupportedOperationException();
}
public long getMaxActive() {
throw new UnsupportedOperationException();
}
public long getMaxActiveTimestamp() {
throw new UnsupportedOperationException();
}
}
}