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>
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.