
net.sf.hulp.profiler.RealProfiler Maven / Gradle / Ivy
The newest version!
// Copyright (c) 1999 Frank Gerard
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
package net.sf.hulp.profiler;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
import net.sf.hulp.util.Markup;
import net.sf.hulp.util.StringTools;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.regex.Pattern;
/**
* A profiler implementation that accumulates data with millisecond or better accuracy.
*/
public class RealProfiler extends Profiler {
private HashMap mTotalers = new HashMap(); // Key=Totaler, Value=Totaler
public static final double[] HISTOGRAM_BINS = new double[30];
private static final double HISTOGRAM_DENOMINATOR = 100.0d;
static {
// Initialize the histogram array
for (int i = 0; i < HISTOGRAM_BINS.length; i++) {
HISTOGRAM_BINS[i] = Math.pow(2.0d, (double) i) / HISTOGRAM_DENOMINATOR;
}
HISTOGRAM_BINS[HISTOGRAM_BINS.length - 1] = Double.MAX_VALUE;
}
/**
* Returns the bin index following bin[i - 1] <= d < bin[i]
*
* @param d value to find
* @return index into bin array
*/
public static final int histoIndex(double d) {
if (d < 0.0d) {
d = 0.0d;
}
if (d > RealProfiler.HISTOGRAM_BINS[RealProfiler.HISTOGRAM_BINS.length - 2]) {
return RealProfiler.HISTOGRAM_BINS.length - 1;
}
// Bin equation:
long k = (long) (HISTOGRAM_DENOMINATOR * d);
int i = 0;
while (k != 0) {
k = (k >> 1);
i++;
}
return i;
}
/**
* One single measurement.
*/
private class RealMeasurementV1 extends net.sf.hulp.measure.Measurement {
private String mTopic;
private String mSubTopic;
private double m_tStart;
private double m_dt;
private boolean mDead;
private Totaler mTotaler;
/**
* Constructor
*/
public RealMeasurementV1(String topic, String subTopic, Totaler a) {
mTopic = topic;
mSubTopic = subTopic;
m_tStart = Chrono.start();
mTotaler = a;
}
/**
* Resets the name as specified
*/
public void setTopic(String topic) {
mTopic = topic;
mTotaler.decActive();
mTotaler = getTotaler(this);
mTotaler.incActive();
}
/**
* Extends the name with a subtopic
*/
public void setSubtopic(String subTopic) {
mSubTopic = subTopic;
mTotaler.decActive();
mTotaler = getTotaler(this);
mTotaler.incActive();
}
/**
* Ends the time interval and adds the measurement to the profiler list.
*/
public void end() {
if (mDead) {
return;
}
mDead = true;
m_dt = Chrono.stop(m_tStart);
if (mTotaler == null) {
mTotaler = getTotaler(this);
}
mTotaler.add(this);
}
/**
* @see java.lang.Object#hashCode()
*
* Note: needs to be identical to Totaler.hashCode so that a lookup can be done
* in the map to find a Totaler using a RealMeasurement as an argument; avoiding
* an extra class (= object) saves on object instantiations
*/
public int hashCode() {
if (mSubTopic != null) {
return mTopic.hashCode() + mSubTopic.hashCode();
}
else if (mTopic != null) {
return mTopic.hashCode();
} else {
return 0;
}
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*
* Note: needs to be identical to Totaler.equals so that a lookup can be done
* in the map to find a Totaler using a RealMeasurement as an argument; avoiding
* an extra class (= object) saves on object instantiations
*/
public boolean equals(Object o) {
if (o instanceof RealMeasurementV1) {
RealMeasurementV1 rhs = (RealMeasurementV1) o;
return (mTopic == rhs.mTopic || (mTopic != null && mTopic.equals(rhs.mTopic)))
&& (mSubTopic == rhs.mSubTopic
|| (mSubTopic != null && rhs.mSubTopic != null && mSubTopic.equals(rhs.mSubTopic)));
} else if (o instanceof Totaler) {
Totaler rhs = (Totaler) o;
return "".equals(rhs.mSource)
&& (mTopic == rhs.mName1 || (mTopic!= null && mTopic.equals(rhs.mName1)))
&& (mSubTopic == rhs.mSubname
|| (mSubTopic != null && rhs.mSubname != null && mSubTopic.equals(rhs.mSubname)));
} else {
return false;
}
}
}
/**
* One single measurement.
*/
private class RealMeasurementV2 extends net.java.hulp.measure.Probe {
private String mSource;
private String mTopic1;
private String mSubTopic;
private double m_tStart;
private double m_dt;
private boolean mDead;
private Totaler mTotaler;
/**
* Constructor
*/
public RealMeasurementV2(Class source, String topic, String subTopic, Totaler a) {
mSource = source == null ? "" : source.getName();
mTopic1 = topic;
mSubTopic = subTopic;
m_tStart = Chrono.start();
mTotaler = a;
}
/**
* Resets the name as specified
*/
public void setTopic(String topic) {
mTopic1 = topic;
mTotaler.decActive();
mTotaler = getTotaler(this);
mTotaler.incActive();
}
/**
* Extends the name with a subtopic
*/
public void setSubtopic(String subTopic) {
mSubTopic = subTopic;
mTotaler.decActive();
mTotaler = getTotaler(this);
mTotaler.incActive();
}
/**
* Ends the time interval and adds the measurement to the profiler list.
*/
public void end() {
if (mDead) {
return;
}
mDead = true;
m_dt = Chrono.stop(m_tStart);
if (mTotaler == null) {
mTotaler = getTotaler(this);
}
mTotaler.add(this);
}
/**
* @see java.lang.Object#hashCode()
*
* Note: needs to be identical to Totaler.hashCode so that a lookup can be done
* in the map to find a Totaler using a RealMeasurement as an argument; avoiding
* an extra class (= object) saves on object instantiations
*/
public int hashCode() {
if (mSubTopic != null) {
return mSource.hashCode() + mTopic1.hashCode() + mSubTopic.hashCode();
}
else if (mTopic1 != null) {
return mSource.hashCode() + mTopic1.hashCode();
} else {
return mSource.hashCode();
}
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*
* Note: needs to be identical to Totaler.equals so that a lookup can be done
* in the map to find a Totaler using a RealMeasurement as an argument; avoiding
* an extra class (= object) saves on object instantiations
*/
public boolean equals(Object o) {
if (o instanceof RealMeasurementV2) {
RealMeasurementV2 rhs = (RealMeasurementV2) o;
return mSource.equals(rhs.mSource)
&& (mTopic1 == rhs.mTopic1 || (mTopic1 != null && mTopic1.equals(rhs.mTopic1)))
&& (mSubTopic == rhs.mSubTopic
|| (mSubTopic != null && rhs.mSubTopic != null && mSubTopic.equals(rhs.mSubTopic)));
} else if (o instanceof Totaler) {
Totaler rhs = (Totaler) o;
return mSource.equals(rhs.mSource)
&& (mTopic1 == rhs.mName1 || (mTopic1 != null && mTopic1.equals(rhs.mName1)))
&& (mSubTopic == rhs.mSubname
|| (mSubTopic != null && rhs.mSubname != null && mSubTopic.equals(rhs.mSubname)));
} else {
return false;
}
}
}
/**
* Accumulates measurements and keeps statistics
*/
private static class Totaler implements Comparable, Cloneable {
private String mSource;
private String mName1;
private String mSubname;
private int mN;
private double m_dtSum;
private double m_tFirst; // Start time of first measurement
private double m_dtFirst; // duration of first measurement
private double m_tLast; // Start time of most recent measurement
private double m_dtLast; // duration of most recent measurement
private int m_nActive;
private int[] mHistogram;
/**
* Constructs a new Totaler
*/
public Totaler(String source, String name, String subname) {
mSource = source;
mName1 = name;
mSubname = subname;
mHistogram = new int[HISTOGRAM_BINS.length];
}
/**
* @see java.lang.Object#clone()
*/
public synchronized Object clone() {
Totaler ret = new Totaler(mSource, mName1, mSubname);
ret.mN = mN;
ret.m_dtSum = m_dtSum;
ret.m_tFirst = m_tFirst;
ret.m_dtFirst = m_dtFirst;
ret.m_tLast = m_tLast;
ret.m_dtLast = m_dtLast;
ret.m_nActive = m_nActive;
ret.mHistogram = new int[mHistogram.length];
System.arraycopy(mHistogram, 0, ret.mHistogram, 0, mHistogram.length);
return ret;
}
private synchronized void incActive() {
m_nActive++;
}
private synchronized void decActive() {
m_nActive--;
}
/**
* Adds a duration to accumulate
*/
private synchronized void add(RealMeasurementV1 d) {
if (mN == 0) {
m_dtFirst = d.m_dt;
m_tFirst = d.m_tStart;
}
mN++;
m_tLast = d.m_tStart;
m_dtLast = d.m_dt;
m_dtSum += d.m_dt;
m_nActive--;
mHistogram[RealProfiler.histoIndex(d.m_dt)]++;
}
/**
* Adds a duration to accumulate
*/
private synchronized void add(RealMeasurementV2 d) {
if (mN == 0) {
m_dtFirst = d.m_dt;
m_tFirst = d.m_tStart;
}
mN++;
m_tLast = d.m_tStart;
m_dtLast = d.m_dt;
m_dtSum += d.m_dt;
m_nActive--;
mHistogram[RealProfiler.histoIndex(d.m_dt)]++;
}
public synchronized double getAverage() {
return m_dtSum / (mN == 0 ? 1 : mN);
}
public synchronized double getAveragePrime() {
return mN <= 1 ? m_dtSum : (m_dtSum - m_dtFirst) / (mN - 1);
}
public synchronized double getLoad() {
double load = Double.NaN;
if (getTimespan() > 1E-8 && getTimespan() != Double.NaN) {
if (mN > 1) {
load = mN * getAverage() / getTimespan();
}
}
return load;
}
public synchronized double getTimespan() {
double dt = Double.NaN;
if (mN >= 1) {
double dtLast = Chrono.stop(m_tLast);
double dtFirst = Chrono.stop(m_tFirst);
dt = dtFirst - dtLast + m_dtLast;
}
return dt;
}
public synchronized double getThroughput() {
double throughput = Double.NaN;
if (getTimespan() > 1E-8 && getTimespan() != Double.NaN) {
throughput = mN / getTimespan() * 1000.0;
}
return throughput;
}
public synchronized double getMedian() {
// Special case: all in first bin
if (mHistogram[0] == mN) {
return 0.0;
}
// Special case: all in last bin
if (mHistogram[HISTOGRAM_BINS.length - 1] == mN) {
return mHistogram[HISTOGRAM_BINS.length - 1];
}
// Find the half point
double half = mN * 0.5d;
int sum = 0;
// Find the x and y coordinates of the point before and after the half
// point so that we can use linear interpolation to find an estimate for
// the half point. X is moving sum, Y = bin number
int x1 = 0;
int y1 = 0;
int x2 = 0;
int y2 = 0;
for (int i = 0; i < mHistogram.length; i++) {
sum += mHistogram[i];
// Special case: exact match
if (sum == half) {
return HISTOGRAM_BINS[i];
}
if (sum <= half) {
x1 = sum;
y1 = i;
} else {
x2 = sum;
y2 = i;
break;
}
}
// Use linear interpolation in the bin number domain to estimate the
// half point
double y = (double) y1
+ ((double) y2 - (double) y1)
/ ((double) x2 - (double) x1)
* (half - (double) x1);
// y now is a fractional bin number. Convert to a time quantity using the
// bin equation
return Math.pow(2.0d, y) / HISTOGRAM_DENOMINATOR;
}
public String getName() {
return mSource + (mSource.length() == 0 ? "" : ".") + mName1;
}
/**
* Prints out a row
*/
synchronized private void dump(final PrintWriter out, final Markup f)
throws IOException {
out.print(f.beginRow()
+ getName()
+ f.tab() + mN
+ f.tab() + Chrono.format(m_dtSum)
+ f.tab() + Chrono.format(getAveragePrime())
+ f.tab() + Chrono.format(getMedian())
+ f.tab() + m_nActive
+ f.tab() + f.fine(mSubname)
+ f.tab() + f.fine(Chrono.format(m_dtFirst))
+ f.tab() + f.fine(Chrono.format(getAverage()))
+ f.tab() + Chrono.format(getThroughput())
+ f.tab() + f.fine(Chrono.format(getTimespan()))
+ f.tab() + f.fine(Chrono.format(getLoad()))
);
for (int i = 0; i < mHistogram.length; i++) {
out.print(f.tab() + mHistogram[i]);
}
out.print(f.endRow());
}
/**
* @see java.lang.Object#hashCode()
*
* Note: needs to be identical to RealMeasurement.hashCode so that a lookup can be done
* in the map to find a Totaler using a RealMeasurement as an argument; avoiding
* an extra class (= object) saves on object instantiations
*/
public int hashCode() {
if (mSubname != null) {
return mSource.hashCode() + mName1.hashCode() + mSubname.hashCode();
}
else if (mName1 != null) {
return mSource.hashCode() + mName1.hashCode();
} else {
return mSource.hashCode();
}
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*
* Note: needs to be identical to RealMeasurement.equals so that a lookup can be done
* in the map to find a Totaler using a RealMeasurement as an argument; avoiding
* an extra class (= object) saves on object instantiations
*/
public boolean equals(Object o) {
if (o instanceof RealMeasurementV1) {
RealMeasurementV1 rhs = (RealMeasurementV1) o;
return "".equals(mSource)
&& (mName1 == rhs.mTopic || (mName1 != null && mName1.equals(rhs.mTopic)))
&& (mSubname == rhs.mSubTopic
|| (mSubname != null && rhs.mSubTopic != null && mSubname.equals(rhs.mSubTopic)));
} else if (o instanceof RealMeasurementV2) {
RealMeasurementV2 rhs = (RealMeasurementV2) o;
return mSource.equals(rhs.mSource)
&& (mName1 == rhs.mTopic1 || (mName1 != null && mName1.equals(rhs.mTopic1)))
&& (mSubname == rhs.mSubTopic
|| (mSubname != null && rhs.mSubTopic != null && mSubname.equals(rhs.mSubTopic)));
} else if (o instanceof Totaler) {
Totaler rhs = (Totaler) o;
return mSource.equals(rhs.mSource)
&& (mName1 == rhs.mName1 || (mName1 != null && mName1.equals(rhs.mName1)))
&& (mSubname == rhs.mSubname
|| (mSubname != null && rhs.mSubname != null && mSubname.equals(rhs.mSubname)));
} else {
return false;
}
}
/**
* @param rhs_ object to compare to
* @return lexical order
*/
public int compareTo(Object rhs_) {
Totaler lhs = this;
Totaler rhs = ((Totaler) rhs_);
int ret = lhs.mSource.compareTo(rhs.mSource);
if (ret == 0) {
if (lhs.mName1 != rhs.mName1) {
if (lhs.mName1 == null) {
ret = -1;
} else if (rhs.mName1 == null) {
ret = 1;
} else {
ret = lhs.mName1.compareTo(rhs.mName1);
}
}
}
if (ret == 0) {
// same... check subtype
if (lhs.mSubname != null && rhs.mSubname != null) {
ret = lhs.mSubname.compareTo(rhs.mSubname);
}
else {
if (lhs.mSubname == null && rhs.mSubname == null) {
ret = 0;
}
if (lhs.mSubname == null) {
ret = 1;
}
else if (rhs.mSubname == null) {
return -1;
}
}
}
return ret;
}
}
private static void header(final PrintWriter out, final Markup f, double overhead)
throws IOException {
// Super header
out.print(f.beginRow(12) + "Profiler data ("
+ StringTools.timeString(System.currentTimeMillis())
+ "; measurement overhead: " + Chrono.format(overhead) + " ms)");
out.print(f.tab(HISTOGRAM_BINS.length) + "Histogram data; bins in ms, counts in n(t)/N");
out.print(f.endRow());
// Column header
out.print(f.beginHRow()
+ H_TOPIC
+ f.tab() + H_N
+ f.tab() + H_TOTALTIME
+ f.tab() + H_AVERAGEPRIME
+ f.tab() + H_MEDIAN
+ f.tab() + H_ACT
+ f.tab() + H_SUBTOPIC
+ f.tab() + f.fine(H_FIRST)
+ f.tab() + f.fine(H_AVERAGE)
+ f.tab() + H_THROUGHPUT
+ f.tab() + f.fine(H_TIMESPAN)
+ f.tab() + f.fine(H_LOAD)
);
for (int i = 0; i < HISTOGRAM_BINS.length; i++) {
out.print(f.tab());
if (HISTOGRAM_BINS[i] == Double.MAX_VALUE) {
out.print("oo");
} else {
out.print(HISTOGRAM_BINS[i]);
}
}
out.print(f.endRow());
}
/**
* Returns an existing or newly created Totaler
*/
private Totaler getTotaler(RealMeasurementV1 m) {
Totaler ret = null;
synchronized (mTotalers) {
ret = (Totaler) mTotalers.get(m);
if (ret == null) {
ret = new Totaler("", m.mTopic, m.mSubTopic);
mTotalers.put(ret, ret);
}
}
return ret;
}
/**
* Returns an existing or newly created Totaler
*/
private Totaler getTotaler(RealMeasurementV2 m) {
Totaler ret = null;
synchronized (mTotalers) {
ret = (Totaler) mTotalers.get(m);
if (ret == null) {
ret = new Totaler(m.mSource, m.mTopic1, m.mSubTopic);
mTotalers.put(ret, ret);
}
}
return ret;
}
/**
* @see net.sf.hulp.measure.Measurement.Factory#create(java.lang.String, java.lang.String)
*/
public net.sf.hulp.measure.Measurement create(String topic, String subTopic) {
RealMeasurementV1 ret = new RealMeasurementV1(topic, subTopic, null);
Totaler a = getTotaler(ret);
a.incActive();
ret.mTotaler = a;
return ret;
}
/**
* @see net.sf.hulp.measure.Measurement.Factory#getData(java.util.List)
*/
@SuppressWarnings("unchecked")
public TabularData getData(List criteria) {
try {
Map m;
synchronized (mTotalers) {
m = (Map) mTotalers.clone();
}
int N = 13;
List ret = new ArrayList();
String[] names = new String[N + HISTOGRAM_BINS.length];
SimpleType[] types = new SimpleType[N + HISTOGRAM_BINS.length];
String[] descrs = new String[N + HISTOGRAM_BINS.length];
CompositeType type = null;
for (Totaler t: m.values()) {
boolean matches = false;
for (Pattern[] p: criteria) {
if (p[0].matcher(t.mSource).matches()
&& p[1].matcher(t.mName1).matches()
&& p[2].matcher(t.mSubname == null ? "" : t.mSubname).matches()) {
matches = true;
break;
}
}
if (matches) {
Object[] values = new Object[N + HISTOGRAM_BINS.length];
int j = 0;
names[j] = H_SOURCE;
types[j] = SimpleType.STRING;
descrs[j] = H_SOURCE;
values[j++] = t.mSource;
names[j] = H_TOPIC;
types[j] = SimpleType.STRING;
descrs[j] = H_TOPIC;
values[j++] = t.mName1;
names[j] = H_N;
types[j] = SimpleType.INTEGER;
descrs[j] = H_N;
values[j++] = t.mN;
names[j] = H_TOTALTIME;
types[j] = SimpleType.DOUBLE;
descrs[j] = H_TOTALTIME;
values[j++] = t.m_dtSum;
names[j] = H_AVERAGEPRIME;
types[j] = SimpleType.DOUBLE;
descrs[j] = H_AVERAGEPRIME;
values[j++] = t.getAveragePrime();
names[j] = H_MEDIAN;
types[j] = SimpleType.DOUBLE;
descrs[j] = H_MEDIAN;
values[j++] = t.getMedian();
names[j] = H_ACT;
types[j] = SimpleType.INTEGER;
descrs[j] = H_ACT;
values[j++] = t.m_nActive;
names[j] = H_SUBTOPIC;
types[j] = SimpleType.STRING;
descrs[j] = H_SUBTOPIC;
values[j++] = t.mSubname == null ? "" : t.mSubname;
names[j] = H_FIRST;
types[j] = SimpleType.DOUBLE;
descrs[j] = H_FIRST;
values[j++] = t.m_dtFirst;
names[j] = H_AVERAGE;
types[j] = SimpleType.DOUBLE;
descrs[j] = H_AVERAGE;
values[j++] = t.getAverage();
names[j] = H_THROUGHPUT;
types[j] = SimpleType.DOUBLE;
descrs[j] = H_THROUGHPUT;
values[j++] = t.getThroughput();
names[j] = H_TIMESPAN;
types[j] = SimpleType.DOUBLE;
descrs[j] = H_TIMESPAN;
values[j++] = t.getTimespan();
names[j] = H_LOAD;
types[j] = SimpleType.DOUBLE;
descrs[j] = H_LOAD;
values[j++] = t.getLoad();
assertTrue(N == j);
for (int i = 0; i < HISTOGRAM_BINS.length; i++) {
names[j] = HISTOGRAM_BINS[i] == Double.MAX_VALUE ? "oo" : "h_" + Double.toString(HISTOGRAM_BINS[i]);
types[j] = SimpleType.INTEGER;
descrs[j] = "Histogram data; bins in ms";
values[j++] = t.mHistogram[i];
}
assertTrue(j == values.length);
if (type == null) {
type = new CompositeType("Performance metrics row", "S", names, descrs, types);
}
CompositeDataSupport row = new CompositeDataSupport(type, names, values);
ret.add(row);
}
}
if (ret.isEmpty()) {
return null;
}
// Calculate overhead
int nTotal = 0;
for (Totaler t: m.values()) {
nTotal += t.mN;
}
double overhead = (double) nTotal * Chrono.getOverheadPerMeasurement();
// Header
String descr = "Profiler data ("
+ StringTools.timeString(System.currentTimeMillis())
+ "; measurement overhead: " + Chrono.format(overhead) + " ms)";
TabularType tabletype = new TabularType(
"Profiler results",
descr,
type, new String[] {H_SOURCE, H_TOPIC, H_SUBTOPIC}
);
TabularData t = new TabularDataSupport(tabletype);
t.putAll(ret.toArray(new CompositeData[0]));
return t;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Dumps out all measurements; clones the table so that measurements can continue.
*/
@SuppressWarnings("unchecked")
public void dump(final PrintWriter out, final Markup f) throws IOException {
Map m = null;
synchronized (mTotalers) {
m = (Map) mTotalers.clone();
}
SortedSet s = new TreeSet(m.values());
// Calculate overhead
int nTotal = 0;
for (Iterator i = s.iterator(); i.hasNext(); ) {
Totaler a = (Totaler) i.next();
nTotal += a.mN;
}
double overhead = (double) nTotal * Chrono.getOverheadPerMeasurement();
// Header
header(out, f, overhead);
// Real data
for (Iterator i = s.iterator(); i.hasNext(); ) {
Totaler a = (Totaler) i.next();
a.dump(out, f);
}
}
/**
* Clears all accumulated data
*/
public void clear() {
synchronized (mTotalers) {
mTotalers.clear();
}
}
@SuppressWarnings("unchecked")
public void clearData(List criteria) {
// Make a list of all objects matching the criteria
List toclear = new ArrayList();
Map m;
synchronized (mTotalers) {
m = (Map) mTotalers.clone();
}
for (Totaler t: m.values()) {
for (Pattern[] p: criteria) {
if (p[0].matcher(t.mSource).matches()
&& p[1].matcher(t.mName1).matches()
&& p[2].matcher(t.mSubname == null ? "" : t.mSubname).matches()) {
toclear.add(t);
break;
}
}
}
// Remove all objects matching the criteria
synchronized (mTotalers) {
for (Totaler t : toclear) {
mTotalers.remove(t);
}
}
}
/**
* @see net.sf.hulp.profiler.Profiler#dump()
*/
public Map[] dump() {
Map m = null;
synchronized (mTotalers) {
m = (Map) mTotalers.clone();
}
Map[] ret = new Map[m.size()];
int i = 0;
for (Iterator iter = m.values().iterator(); iter.hasNext();) {
Totaler t = (Totaler) iter.next();
Map e = new HashMap();
ret[i++] = e;
e.put(H_TOPIC, t.getName());
e.put(H_SUBTOPIC, t.mSubname);
e.put(H_N, new Integer(t.mN));
e.put(H_TOTALTIME, new Double(t.m_dtSum));
e.put(H_AVERAGEPRIME, new Double(t.getAveragePrime()));
e.put(H_ACT, new Integer(t.m_nActive));
e.put(H_THROUGHPUT, new Double(t.getThroughput()));
e.put(H_FIRST, new Double(t.m_dtFirst));
e.put(H_AVERAGE, new Double(t.getAverage()));
e.put(H_TIMESPAN, new Double(t.getTimespan()));
e.put(H_LOAD, new Double(t.getLoad()));
}
return ret;
}
/**
* @see net.sf.hulp.profiler.Profiler#dump(java.lang.String)
*/
@SuppressWarnings("unchecked")
public Map dump(String topicSubtopicSeparator) {
Map> ret = new HashMap();
Map[] entries = dump();
for (int i = 0; i < entries.length; i++) {
String key = entries[i].get(H_TOPIC) + topicSubtopicSeparator + entries[i].get(H_SUBTOPIC);
ret.put(key, entries[i]);
}
return ret;
}
/**
* For testing
*/
private static void assertTrue(boolean b) {
if (!b) {
throw new RuntimeException();
}
}
/**
* For testing
*/
public void testCompare() {
RealMeasurementV1 a = new RealMeasurementV1(null, null, null);
Totaler b = new Totaler("", null, null);
assertTrue(a.equals(b));
a.mTopic = "a"; //a=a,null; b=null,null
assertTrue(!a.equals(b));
assertTrue(!b.equals(a));
assertTrue(a.hashCode() != b.hashCode());
a.mSubTopic = "b"; //a=a,b; b=null,null
assertTrue(!a.equals(b));
assertTrue(!b.equals(a));
assertTrue(a.hashCode() != b.hashCode());
b.mName1 = "a"; //a=a,b; b=a,null
assertTrue(!a.equals(b));
assertTrue(!b.equals(a));
assertTrue(a.hashCode() != b.hashCode());
b.mSubname = "b"; //a=a,b; b=a,b
assertTrue(a.equals(b));
assertTrue(b.equals(a));
assertTrue(a.hashCode() == b.hashCode());
a.mSubTopic = null; //a=a,null; b=a,b
assertTrue(!a.equals(b));
assertTrue(!b.equals(a));
assertTrue(a.hashCode() != b.hashCode());
}
public void help(PrintWriter out, Markup f) {
if (!net.sf.hulp.measure.Measurement.isInstalled()) {
out.println("All classes are available but the profiler is not enabled." + f.br());
out.println("Set the following argument to the JVM:
");
out.println(f.beginArea());
out.println(f.beginPre());
out.println("-Dnet.sf.hulp.profiler=1");
out.println(f.endPre() + f.br());
out.println(f.endArea());
} else {
out.println(f.beginTable(Markup.TABLESTYLE_SIMPLE));
out.println(f.beginRow() + "Profiler enabled" + f.tab() + "true" + f.endRow());
out.println(f.beginRow() + "High resolution timer available" + f.tab() + Chrono.isHighResTimerAvailable() + f.endRow());
out.println(f.beginRow() + "Timer overhead" + f.tab() + Chrono.getOverheadPerMeasurement() + " ms per measurement" + f.endRow());
out.println(f.endTable());
out.println(f.br());
out.println(f.br());
out.println("The profiler is based on time probes that are placed strategically in");
out.println("the code base. The probles have a name (topic) and an optional sub-name");
out.println("(subtopic). A probe essentially measures the time difference between a");
out.println("start and stop time, just like a stopwatch. The profiler aggregates");
out.println("these measured time differences dt.
");
out.println("
");
out.println("Click on dump to get a listing");
out.println("of all aggregated results of all probes in HTML format.
");
out.println("Click on dump text to get the");
out.println("same data in tab-delimited text; this can be used in a spreadsheet.
");
out.println("Click on clear to reset all");
out.println("probes and remove them from memory
");
out.println("
");
out.println("The following columns are displayed in the dump:
");
out.println("
");
out.println("");
out.println("");
out.println("");
out.println("n ");
out.println("Number of measurements (or N), i.e. the number");
out.println("of dt-s, i.e. the number of times that Measurement.begin() - end() was called.
");
out.println(" ");
out.println(" ");
out.println("");
out.println("total");
out.println("time (ms) ");
out.println("the sum of all dt-s ");
out.println(" ");
out.println("");
out.println("average'");
out.println("(ms) ");
out.println("(the sum of all dt-s minus the first dt)");
out.println("divided by N.");
out.println("The first measurement is discounted because it typically includes");
out.println("classloading times and distorts the results considerably. If there's");
out.println("only one measurement, the first measurement is not discounted and the");
out.println("value should be equal to total time ");
out.println(" ");
out.println("");
out.println("act ");
out.println("the number of measurement objects on which begin()");
out.println("was called but not end().");
out.println("This indicates the number of active measurements ");
out.println(" ");
out.println("");
out.println("sub");
out.println("topic ");
out.println("the name of the measurement specified in the second");
out.println("argument of begin() or in setSubTopic() ");
out.println(" ");
out.println("");
out.println("first ");
out.println("the first dt. This is interesting because it");
out.println("may include special initializations
");
out.println(" ");
out.println(" ");
out.println("");
out.println("average ");
out.println("sum of all dt-s divided by N; this does not");
out.println("discount the first measurement ");
out.println(" ");
out.println("");
out.println("throughput ");
out.println("N divided by");
out.println("(tlast - tfirst); this is the average throughput.");
out.println("This number is meaningful if there were no long pauses in");
out.println("processing. ");
out.println(" ");
out.println("");
out.println("tlast");
out.println("- tfirst ");
out.println("the wallclock time of the first measurement's begin()");
out.println("method is tracked as tfirst and the wallclock time of the");
out.println("last measurement's end() method is tracked as tlast ");
out.println(" ");
out.println("");
out.println("Load ");
out.println("The sum of all dt-s divided by (tlast");
out.println("- tfirst).");
out.println("This is a measure of concurrency: the higher the number, the greater");
out.println("the concurrency. In a single threaded scenario this number can never");
out.println("exceed 1. ");
out.println(" ");
out.println("");
out.println("Histograms ");
out.println("See below
");
out.println(" ");
out.println(" ");
out.println("");
out.println("
");
out.println("
");
out.println("Data is tracked for histograms. There are 30 bins (0 - 29). The times");
out.println("are on an exponential scale: 0.01 * 2bin where bin is the bin number.");
out.println("");
}
}
public net.java.hulp.measure.Probe createV2(int level, Class source, String topic, String subtopic) {
RealMeasurementV2 ret = new RealMeasurementV2(source, topic, subtopic, null);
Totaler a = getTotaler(ret);
a.incActive();
ret.mTotaler = a;
return ret;
}
//The profiler is based on time probes that are placed strategically in
//the code base. The probles have a name (topic) and an optional sub-name
//(subtopic). A probe essentially measures the time difference between a
//start and stop time, just like a stopwatch. The profiler aggregates
//these measured time differences dt.
//
//Click on dump to get a listing
//of all aggregated results of all probes in HTML format.
//Click on dump text to get the
//same data in tab-delimited text; this can be used in a spreadsheet.
//Click on clear to reset all
//probes and remove them from memory
//
//The following columns are displayed in the dump:
//
//
//
//
//n
//Number of measurements (or N), i.e. the number
//of dt-s, i.e. the number of times that Measurement.begin() - end() was called.
//
//
//
//total
//time (ms)
//the sum of all dt-s
//
//
//average'
//(ms)
//(the sum of all dt-s minus the first dt)
//divided by N.
//The first measurement is discounted because it typically includes
//classloading times and distorts the results considerably. If there's
//only one measurement, the first measurement is not discounted and the
//value should be equal to total time
//
//
//act
//the number of measurement objects on which begin()
//was called but not end().
//This indicates the number of active measurements
//
//
//sub
//topic
//the name of the measurement specified in the second
//argument of begin() or in setSubTopic()
//
//
//first
//the first dt. This is interesting because it
//may include special initializations
//
//
//
//average
//sum of all dt-s divided by N; this does not
//discount the first measurement
//
//
//throughput
//N divided by
//(tlast - tfirst); this is the average throughput.
//This number is meaningful if there were no long pauses in
//processing.
//
//
//tlast
//- tfirst
//the wallclock time of the first measurement's begin()
//method is tracked as tfirst and the wallclock time of the
//last measurement's end() method is tracked as tlast
//
//
//Load
//The sum of all dt-s divided by (tlast
//- tfirst).
//This is a measure of concurrency: the higher the number, the greater
//the concurrency. In a single threaded scenario this number can never
//exceed 1.
//
//
//Histograms
//See below
//
//
//
//
//
//Data is tracked for histograms. There are 30 bins (0 - 29). The times
//are on an exponential scale: 0.01 * 2bin where bin is the bin number.
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy