Author: sebb
Date: Tue Dec 15 00:53:43 2009
New Revision: 890589
URL: http://svn.apache.org/viewvc?rev=890589&view=rev
Log:
Bug 48259 - Improve StatCalculator performance by using HashMap
Modified:
jakarta/jmeter/trunk/src/jorphan/org/apache/jorphan/math/StatCalculator.java
jakarta/jmeter/trunk/xdocs/changes.xml
Modified:
jakarta/jmeter/trunk/src/jorphan/org/apache/jorphan/math/StatCalculator.java
URL:
http://svn.apache.org/viewvc/jakarta/jmeter/trunk/src/jorphan/org/apache/jorphan/math/StatCalculator.java?rev=890589&r1=890588&r2=890589&view=diff
==============================================================================
---
jakarta/jmeter/trunk/src/jorphan/org/apache/jorphan/math/StatCalculator.java
(original)
+++
jakarta/jmeter/trunk/src/jorphan/org/apache/jorphan/math/StatCalculator.java
Tue Dec 15 00:53:43 2009
@@ -18,11 +18,10 @@
package org.apache.jorphan.math;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
+import java.util.TreeSet;
+
+import org.apache.commons.lang.mutable.MutableLong;
/**
* This class serves as a way to calculate the median, max, min etc. of a list
of values.
@@ -31,8 +30,10 @@
*/
public abstract class StatCalculator<T extends Number & Comparable<? super T>>
{
- private final List<T> values = new ArrayList<T>();
+ // key is the type to collect (usually long), value = count of entries
+ private final HashMap<T, MutableLong> valuesMap = new HashMap<T,
MutableLong>();
+ // Running values, updated for each sample
private double sum = 0;
private double sumOfSquares = 0;
@@ -43,13 +44,19 @@
private int count = 0;
+ private T min;
+
+ private T max;
+
+ private transient TreeSet<T> sortedKeys; // cached sorted set
+
private long bytes = 0;
private final T ZERO;
- private final T MAX_VALUE;
+ private final T MAX_VALUE; // e.g. Long.MAX_VALUE
- private final T MIN_VALUE;
+ private final T MIN_VALUE; // e.g. Long.MIN_VALUE
/**
* This constructor is used to set up particular values for the generic
class instance.
@@ -58,20 +65,27 @@
* @param min - value to return for minimum if there are no values
* @param max - value to return for maximum if there are no values
*/
- public StatCalculator(T zero, T min, T max) {
+ public StatCalculator(final T zero, final T min, final T max) {
super();
ZERO = zero;
MAX_VALUE = max;
MIN_VALUE = min;
+ this.min = MAX_VALUE;
+ this.max = MIN_VALUE;
+ sortedKeys = null;
}
public void clear() {
- values.clear();
+ valuesMap.clear();
+ sortedKeys = null;
sum = 0;
sumOfSquares = 0;
mean = 0;
deviation = 0;
count = 0;
+ bytes = 0;
+ max = MIN_VALUE;
+ min = MAX_VALUE;
}
@@ -80,17 +94,13 @@
}
public void addAll(StatCalculator<T> calc) {
- Iterator<T> iter = calc.values.iterator();
- while (iter.hasNext()) {
- addValue(iter.next());
+ for (T val : calc.valuesMap.keySet()) {
+ addValue(val);
}
}
public T getMedian() {
- if (count > 0) {
- return values.get((int) (values.size() * .5));
- }
- return ZERO;
+ return getPercentPoint(0.5);
}
public long getTotalBytes() {
@@ -107,10 +117,7 @@
* @return number of values less than the percentage
*/
public T getPercentPoint(float percent) {
- if (count > 0) {
- return values.get((int) (values.size() * percent));
- }
- return ZERO;
+ return getPercentPoint((double) percent);
}
/**
@@ -120,42 +127,45 @@
* are below, the remaining 10% are above.
*
* @param percent
- * @return number of values less than the percentage
+ * @return the value which %percent% of the values are less than
*/
public T getPercentPoint(double percent) {
- if (count > 0) {
- return values.get((int) (values.size() * percent));
+ if (count <= 0) {
+ return ZERO;
+ }
+ if (percent >= 1.0) {
+ return getMax();
}
- return ZERO;
+
+ // use Math.round () instead of simple (long) to provide correct value
rounding
+ long target = Math.round (count * percent);
+ if (sortedKeys == null){
+ sortedKeys = new TreeSet<T> (valuesMap.keySet());
+ }
+ for (T val : sortedKeys) {
+ target -= valuesMap.get(val).longValue();
+ if (target <= 0){
+ return val;
+ }
+ }
+ return ZERO; // TODO should this be getMin()?
}
/**
* Returns the distribution of the values in the list.
*
- * TODO round values to reduce the number of distinct entries.
- *
* @return map containing either Integer or Long keys; entries are a
Number array containing the key and the [Integer] count.
* TODO - why is the key value also stored in the entry array?
*/
public synchronized HashMap<Number, Number[]> getDistribution() {
- HashMap<Number, Number[]> items = new HashMap<Number, Number[]>();
- Iterator<T> itr = this.values.iterator();
+ HashMap<Number, Number[]> items = new HashMap <Number, Number[]> ();
Number[] dis;
- while (itr.hasNext()) {
- Number nx = itr.next();
- if (!(nx instanceof Integer || nx instanceof Long)){
- nx=new Long(nx.longValue()); // convert to Long unless Integer
or Long
- }
- if (items.containsKey(nx)) {
- dis = items.get(nx);
- dis[1] = new Integer(dis[1].intValue() + 1);
- items.put(nx, dis);
- } else {
- dis = new Number[2];
- dis[0] = nx;
- dis[1] = new Integer(1);
- items.put(nx, dis);
- }
+
+ for (T nx : valuesMap.keySet()) {
+ dis = new Number[2];
+ dis[0] = nx;
+ dis[1] = valuesMap.get(nx);
+ items.put(nx, dis);
}
return items;
}
@@ -169,17 +179,11 @@
}
public T getMin() {
- if (count > 0) {
- return values.get(0);
- }
- return MIN_VALUE;
+ return min;
}
public T getMax() {
- if (count > 0) {
- return values.get(count - 1);
- }
- return MAX_VALUE;
+ return max;
}
public int getCount() {
@@ -187,23 +191,29 @@
}
public void addValue(T val) {
- addSortedValue(val);
+ sortedKeys = null;
+ updateValueCount(val);
count++;
double currentVal = val.doubleValue();
sum += currentVal;
sumOfSquares += currentVal * currentVal;
mean = sum / count;
deviation = Math.sqrt((sumOfSquares / count) - (mean * mean));
+ if (val.compareTo(max) > 0){
+ max=val;
+ }
+ if (val.compareTo(min) < 0){
+ min=val;
+ }
}
- private void addSortedValue(T val) {
- int index = Collections.binarySearch(values, val);
- if (index >= 0 && index < values.size()) {
- values.add(index, val);
- } else if (index == values.size() || values.size() == 0) {
- values.add(val);
+ private void updateValueCount(T val) {
+ MutableLong count = valuesMap.get(val);
+ if (count != null) {
+ count.increment();
} else {
- values.add((index * (-1)) - 1, val);
+ // insert new value
+ valuesMap.put(val, new MutableLong(1L));
}
}
}
Modified: jakarta/jmeter/trunk/xdocs/changes.xml
URL:
http://svn.apache.org/viewvc/jakarta/jmeter/trunk/xdocs/changes.xml?rev=890589&r1=890588&r2=890589&view=diff
==============================================================================
--- jakarta/jmeter/trunk/xdocs/changes.xml (original)
+++ jakarta/jmeter/trunk/xdocs/changes.xml Tue Dec 15 00:53:43 2009
@@ -154,6 +154,7 @@
<li>Bug 47952 - Added JSR223 Listener</li>
<li>Bug 47474 - View Results Tree support for plugin renderers</li>
<li>Allow Idle Time to be saved to sample log files</li>
+<li>Bug 48259 - Improve StatCalculator performance by using HashMap</li>
</ul>
<h3>Timers, Assertions, Config, Pre- & Post-Processors</h3>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]