Break the stat collection out from MethodProfiler, and use a calculating map from MapMaker to keep

from needing to synchronize around profile access.



git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6018 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Charlie Groves
2010-01-22 03:53:25 +00:00
parent d86971592a
commit 9c92618773
2 changed files with 75 additions and 70 deletions
@@ -21,9 +21,9 @@
package com.threerings.util; package com.threerings.util;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
@@ -51,13 +51,13 @@ public class MethodProfiler
} }
/** Number of method calls sampled. */ /** Number of method calls sampled. */
public int numSamples; public final int numSamples;
/** Average time spent in the method. */ /** Average time spent in the method. */
public double averageTime; public final double averageTime;
/** Standard deviation from the average. */ /** Standard deviation from the average. */
public double standardDeviation; public final double standardDeviation;
// from Object // from Object
@Override @Override
@@ -117,30 +117,17 @@ public class MethodProfiler
} }
} }
/**
* Creates a new profiler.
*/
public MethodProfiler ()
{
}
/** /**
* Gets all method results so far. * Gets all method results so far.
*/ */
public Map<String, Result> getResults () public Map<String, Result> getResults ()
{ {
HashMap<String, MethodProfile> profiles = new HashMap<String, MethodProfile>(); Map<String, Result> results = Maps.newHashMapWithExpectedSize(_profiles.size());
synchronized (_profiles) { for (Map.Entry<String, RunningStats> entry : _profiles.entrySet()) {
profiles.putAll(_profiles);
}
HashMap<String, Result> results = new HashMap<String, Result>();
for (Map.Entry<String, MethodProfile> entry : profiles.entrySet()) {
synchronized (entry.getValue()) { synchronized (entry.getValue()) {
results.put(entry.getKey(), entry.getValue().toResult()); results.put(entry.getKey(), toResult(entry.getValue()));
} }
} }
return results; return results;
} }
@@ -204,9 +191,7 @@ public class MethodProfiler
*/ */
public void reset () public void reset ()
{ {
synchronized (_profiles) { _profiles.clear();
_profiles.clear();
}
} }
/** /**
@@ -214,16 +199,9 @@ public class MethodProfiler
*/ */
protected void recordTime (String method, double elapsedMs) protected void recordTime (String method, double elapsedMs)
{ {
MethodProfile profile; RunningStats stats = _profiles.get(method);
synchronized (_profiles) { synchronized (stats) {
profile = _profiles.get(method); stats.addSample(elapsedMs);
if (profile == null) {
_profiles.put(method, profile = new MethodProfile());
}
}
synchronized (profile) {
profile.addSample(elapsedMs);
} }
} }
@@ -320,48 +298,26 @@ public class MethodProfiler
protected Method[] _methods = {new Method()}; protected Method[] _methods = {new Method()};
} }
/**
* Holds the aggregate information about all the calls to a given method. The caller is
* expected to perform all thread coordination.
*/
protected static class MethodProfile
{
/**
* Records a new elapsed time spent in the method.
*/
public void addSample (double time)
{
// From http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm
_numSamples++;
double deltaToOld = time - _average;
_average += deltaToOld / _numSamples;
double deltaToNew = time - _average;
_varianceSum += deltaToOld * deltaToNew;
}
/**
* Calculates the results of the the profile.
*/
public Result toResult ()
{
return new Result(_numSamples, _average, Math.sqrt(_varianceSum / _numSamples));
}
protected int _numSamples;
protected double _average;
protected double _varianceSum;
}
/** /**
* Runs the method profile stat collection with the given samples and logs the result. * Runs the method profile stat collection with the given samples and logs the result.
*/ */
protected static void simpleSampleTest (String name, double... samples) protected static void simpleSampleTest (String name, double... samples)
{ {
MethodProfile prof = new MethodProfile(); RunningStats stats = new RunningStats();
for (double sample : samples) { for (double sample : samples) {
prof.addSample(sample); stats.addSample(sample);
} }
log.info(name, "results", prof.toResult()); log.info(name, "results", toResult(stats));
}
/**
* Calculates the results of the the profile.
*/
protected static Result toResult (RunningStats stats)
{
return new Result(stats.getNumSamples(), stats.getMean(), stats.getStandardDeviation());
} }
/** Set of active methods in the current thread. */ /** Set of active methods in the current thread. */
@@ -371,6 +327,7 @@ public class MethodProfiler
} }
}; };
/** Profiles by method name. */ /** Stats by method name. */
protected HashMap<String, MethodProfile> _profiles = Maps.newHashMap(); protected final Map<String, RunningStats> _profiles =
new MapMaker().makeComputingMap(DefaultMap.newInstanceCreator(RunningStats.class));
} }
@@ -0,0 +1,48 @@
package com.threerings.util;
/**
* Calculates live values for the mean, variance and standard deviation of a set of samples.
* <em>Not thread safe!</em>
*/
public class RunningStats
{
/**
* Adds a new sample.
*/
public void addSample (double sample)
{
// From http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm
_numSamples++;
double deltaToOld = sample - _mean;
_mean += deltaToOld / _numSamples;
double deltaToNew = sample - _mean;
_varianceSum += deltaToOld * deltaToNew;
}
public double getVariance ()
{
if (getNumSamples() == 0) {
return 0;
}
return _varianceSum / getNumSamples();
}
public int getNumSamples ()
{
return _numSamples;
}
public double getMean ()
{
return _mean;
}
public double getStandardDeviation ()
{
return Math.sqrt(getVariance());
}
protected int _numSamples;
protected double _mean;
protected double _varianceSum;
}