From 9c92618773beffa3bf57c8bf93d2085feef09d1a Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Fri, 22 Jan 2010 03:53:25 +0000 Subject: [PATCH] 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 --- .../com/threerings/util/MethodProfiler.java | 97 ++++++------------- .../com/threerings/util/RunningStats.java | 48 +++++++++ 2 files changed, 75 insertions(+), 70 deletions(-) create mode 100644 src/java/com/threerings/util/RunningStats.java diff --git a/src/java/com/threerings/util/MethodProfiler.java b/src/java/com/threerings/util/MethodProfiler.java index bc55ed32b..c9e047062 100644 --- a/src/java/com/threerings/util/MethodProfiler.java +++ b/src/java/com/threerings/util/MethodProfiler.java @@ -21,9 +21,9 @@ package com.threerings.util; -import java.util.HashMap; import java.util.Map; +import com.google.common.collect.MapMaker; import com.google.common.collect.Maps; import com.samskivert.util.StringUtil; @@ -51,13 +51,13 @@ public class MethodProfiler } /** Number of method calls sampled. */ - public int numSamples; + public final int numSamples; /** Average time spent in the method. */ - public double averageTime; + public final double averageTime; /** Standard deviation from the average. */ - public double standardDeviation; + public final double standardDeviation; // from Object @Override @@ -117,30 +117,17 @@ public class MethodProfiler } } - /** - * Creates a new profiler. - */ - public MethodProfiler () - { - } - /** * Gets all method results so far. */ public Map getResults () { - HashMap profiles = new HashMap(); - synchronized (_profiles) { - profiles.putAll(_profiles); - } - - HashMap results = new HashMap(); - for (Map.Entry entry : profiles.entrySet()) { + Map results = Maps.newHashMapWithExpectedSize(_profiles.size()); + for (Map.Entry entry : _profiles.entrySet()) { synchronized (entry.getValue()) { - results.put(entry.getKey(), entry.getValue().toResult()); + results.put(entry.getKey(), toResult(entry.getValue())); } } - return results; } @@ -204,9 +191,7 @@ public class MethodProfiler */ public void reset () { - synchronized (_profiles) { - _profiles.clear(); - } + _profiles.clear(); } /** @@ -214,16 +199,9 @@ public class MethodProfiler */ protected void recordTime (String method, double elapsedMs) { - MethodProfile profile; - synchronized (_profiles) { - profile = _profiles.get(method); - if (profile == null) { - _profiles.put(method, profile = new MethodProfile()); - } - } - - synchronized (profile) { - profile.addSample(elapsedMs); + RunningStats stats = _profiles.get(method); + synchronized (stats) { + stats.addSample(elapsedMs); } } @@ -320,48 +298,26 @@ public class MethodProfiler 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. */ protected static void simpleSampleTest (String name, double... samples) { - MethodProfile prof = new MethodProfile(); + RunningStats stats = new RunningStats(); 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. */ @@ -371,6 +327,7 @@ public class MethodProfiler } }; - /** Profiles by method name. */ - protected HashMap _profiles = Maps.newHashMap(); + /** Stats by method name. */ + protected final Map _profiles = + new MapMaker().makeComputingMap(DefaultMap.newInstanceCreator(RunningStats.class)); } diff --git a/src/java/com/threerings/util/RunningStats.java b/src/java/com/threerings/util/RunningStats.java new file mode 100644 index 000000000..685cbbd3b --- /dev/null +++ b/src/java/com/threerings/util/RunningStats.java @@ -0,0 +1,48 @@ +package com.threerings.util; + +/** + * Calculates live values for the mean, variance and standard deviation of a set of samples. + * Not thread safe! + */ +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; +} \ No newline at end of file