001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.performance;
018
019 import java.util.ArrayList;
020 import java.util.HashMap;
021 import java.util.Iterator;
022 import java.util.List;
023 import java.io.Serializable;
024 import org.apache.commons.math.stat.descriptive.SummaryStatistics;
025
026 /**
027 * <p>Container for {@link SummaryStatistics} accumulated during
028 * {@link ClientThread} executions.</p>
029 *
030 * <p>Maintains a HashMap of {@link SummaryStatistics} instances with a composite
031 * key of the form (process,type). "Process" typically identifies the client
032 * thread and "type" identifies the metric - e.g., "latency", "numActive."</p>
033 *
034 * <p>{@link ClientThread#run()} adds one <code>SummaryStatistics</code>
035 * instance, with key = (current thread id,"latency").</p>
036 *
037 */
038 public class Statistics implements Serializable {
039 private HashMap<StatisticsKey,SummaryStatistics> data =
040 new HashMap <StatisticsKey,SummaryStatistics>();
041
042 /**
043 * Adds the results of the given SummaryStatistics instance under
044 * the key <process,type>
045 *
046 * @param stats the SummaryStatistics whose results we are adding
047 * @param process name of the associated process
048 * @param type description of the associated metric
049 */
050 public synchronized void addStatistics(SummaryStatistics stats,
051 String process, String type) {
052 StatisticsKey key = new StatisticsKey(process, type);
053 data.put(key, stats);
054 }
055
056 /**
057 * Retrieves the SummaryStatistics corresponding to the given
058 * process and type, if this exists; null otherwise.
059 *
060 * @param process name of the associated process
061 * @param type description of the associated metric
062 * @return SummaryStatistics for the given <process,type>; null if there is
063 * no such element in the container
064 */
065 public synchronized SummaryStatistics getStatistics(String process,
066 String type) {
067 StatisticsKey key = new StatisticsKey(process, type);
068 return data.get(key);
069 }
070
071 /**
072 * Returns the full list of SummaryStatistics corresponding to
073 * the given <code>type</code> - i.e, the list of statistics of the
074 * given type across processes. For example,
075 * <code>getStatisticsByType("latency")</code> will return a list of latency
076 * summaries, one for each process, assuming "latency" is the name of
077 * an accumulated metric.
078 *
079 * @param type the type value to get statistics for
080 * @return the List of SummaryStatistics stored under the given type
081 */
082 public synchronized List<SummaryStatistics> getStatisticsByType(String type) {
083 ArrayList<SummaryStatistics> result = new ArrayList<SummaryStatistics>();
084 Iterator<StatisticsKey> it = data.keySet().iterator();
085 while (it.hasNext()) {
086 StatisticsKey key = it.next();
087 if (key.type.equals(type)) {
088 result.add(data.get(key));
089 }
090 }
091 return result;
092 }
093
094 /**
095 * Returns the full list of SummaryStatistics corresponding to
096 * the given <code>process</code> - i.e, the list of statistics of
097 * of different types maintained for the given process.
098 *
099 * @param process the process to get statistics for
100 * @return the List of SummaryStatistics for the given process
101 */
102 public synchronized List<SummaryStatistics> getStatisticsByProcess(
103 String process) {
104 ArrayList<SummaryStatistics> result = new ArrayList<SummaryStatistics>();
105 Iterator<StatisticsKey> it = data.keySet().iterator();
106 while (it.hasNext()) {
107 StatisticsKey key = it.next();
108 if (key.process.equals(process)) {
109 result.add(data.get(key));
110 }
111 }
112 return result;
113 }
114
115 /**
116 * <p>Returns a SummaryStatistics instance describing the mean of the given
117 * metric across processes - i.e., the "mean of the means", the
118 * "min of the means" etc. More precisely, the returned SummaryStatistics
119 * describes the distribution of the individual process means for the given
120 * metric.</p>
121 *
122 * <p>The same results could be obtained by iterating over the result of
123 * {{@link #getStatisticsByType(String)} for the given <code>type</code>,
124 * extracting the mean and adding its value to a SummaryStatistics
125 * instance.</p>
126 *
127 * @param type the metric to get summary mean statistics for
128 * @return a SummaryStatistics instance describing the process means for
129 * the given metric
130 */
131 public synchronized SummaryStatistics getMeanSummary(String type) {
132 SummaryStatistics result = new SummaryStatistics();
133 Iterator<StatisticsKey> it = data.keySet().iterator();
134 while (it.hasNext()) {
135 StatisticsKey key = it.next();
136 if (key.type.equals(type)) {
137 result.addValue(data.get(key).getMean());
138 }
139 }
140 return result;
141 }
142
143 /**
144 * Returns SummaryStatistics for the standard deviation of the given metric
145 * across processes.
146 *
147 * @param type the metric to get summary standard deviation statistics for
148 * @return a SummaryStatistics instance describing the process standard
149 * deviations for the given metric
150 * @see #getMeanSummary(String)
151 */
152 public synchronized SummaryStatistics getStdSummary(String type) {
153 SummaryStatistics result = new SummaryStatistics();
154 Iterator<StatisticsKey> it = data.keySet().iterator();
155 while (it.hasNext()) {
156 StatisticsKey key = it.next();
157 if (key.type.equals(type)) {
158 result.addValue(data.get(key).getStandardDeviation());
159 }
160 }
161 return result;
162 }
163
164 /**
165 * Returns SummaryStatistics for the minimum of the given metric across
166 * processes.
167 *
168 * @param type the metric to get summary minimum statistics for
169 * @return a SummaryStatistics instance describing the process minima
170 * for the given metric
171 * @see #getMeanSummary(String)
172 */
173 public synchronized SummaryStatistics getMinSummary(String type) {
174 SummaryStatistics result = new SummaryStatistics();
175 Iterator<StatisticsKey> it = data.keySet().iterator();
176 while (it.hasNext()) {
177 StatisticsKey key = it.next();
178 if (key.type.equals(type)) {
179 result.addValue(data.get(key).getMin());
180 }
181 }
182 return result;
183 }
184
185 /**
186 * Returns SummaryStatistics for the maximum of the given metric across
187 * processes.
188 *
189 * @param type the metric to get summary maximum statistics for
190 * @return a SummaryStatistics describing the process maxima
191 * for the given metric
192 * @see #getMeanSummary(String)
193 */
194 public synchronized SummaryStatistics getMaxSummary(String type) {
195 SummaryStatistics result = new SummaryStatistics();
196 Iterator<StatisticsKey> it = data.keySet().iterator();
197 while (it.hasNext()) {
198 StatisticsKey key = it.next();
199 if (key.type.equals(type)) {
200 result.addValue(data.get(key).getMax());
201 }
202 }
203 return result;
204 }
205
206 /**
207 * Returns the List of processes corresponding to statistics.
208 *
209 * @return List of processes represented in the container
210 */
211 public synchronized List<String> getProcesses() {
212 ArrayList<String> result = new ArrayList<String>();
213 Iterator<StatisticsKey> it = data.keySet().iterator();
214 while (it.hasNext()) {
215 String currProcess = it.next().process;
216 if (!result.contains(currProcess)) {
217 result.add(currProcess);
218 }
219 }
220 return result;
221 }
222
223 /**
224 * Returns the List of types corresponding to statistics.
225 *
226 * @return List of types represented in the container
227 */
228 public synchronized List<String> getTypes() {
229 ArrayList<String> result = new ArrayList<String>();
230 Iterator<StatisticsKey> it = data.keySet().iterator();
231 while (it.hasNext()) {
232 String currType = it.next().type;
233 if (!result.contains(currType)) {
234 result.add(currType);
235 }
236 }
237 return result;
238 }
239
240 /**
241 * Computes and formats display of summary statistics by type, across
242 * processes.
243 *
244 * @return String representing summaries for each metric
245 */
246 public synchronized String displayOverallSummary() {
247 Iterator<String> metricsIterator = getTypes().iterator();
248 StringBuffer buffer = new StringBuffer();
249 while (metricsIterator.hasNext()) {
250 String metric = metricsIterator.next();
251 buffer.append("Overall statistics for the mean ");
252 buffer.append(metric.toUpperCase());
253 buffer.append("\n");
254 buffer.append(getMeanSummary(metric).toString());
255 buffer.append("********************************************\n");
256 buffer.append("Overall statistics for the standard deviation ");
257 buffer.append(metric.toUpperCase());
258 buffer.append("\n");
259 buffer.append(getStdSummary(metric).toString());
260 buffer.append("********************************************\n");
261 buffer.append("Overall statistics for the min ");
262 buffer.append(metric.toUpperCase());
263 buffer.append("\n");
264 buffer.append(getMinSummary(metric).toString());
265 buffer.append("********************************************\n");
266 buffer.append("Overall statistics for the max ");
267 buffer.append(metric.toUpperCase());
268 buffer.append("\n");
269 buffer.append(getMaxSummary(metric).toString());
270 buffer.append("********************************************\n");
271 }
272 return buffer.toString();
273 }
274
275 /**
276 * Displays statistics for the given process
277 *
278 * @param process the process to retrieve metrics for
279 * @return String representing all currently defined statistics for the
280 * given process
281 */
282 public synchronized String displayProcessStatistics(String process) {
283 Iterator<String> metricsIterator = getTypes().iterator();
284 StringBuffer buffer = new StringBuffer();
285 while (metricsIterator.hasNext()) {
286 String metric = metricsIterator.next();
287 buffer.append("*********************************************\n");
288 buffer.append(metric.toUpperCase());
289 buffer.append(" for ");
290 buffer.append(process);
291 buffer.append(" ");
292 buffer.append(getStatistics(process, metric).toString());
293 buffer.append("\n********************************************\n");
294 }
295 return buffer.toString();
296 }
297
298
299 /**
300 * Composite key (<process,type>).
301 */
302 private static class StatisticsKey implements Serializable {
303 public StatisticsKey(String process, String type) {
304 this.process = process;
305 this.type = type;
306 }
307 private String process = null;
308 private String type = null;
309 public boolean equals(Object obj) {
310 if (!(obj instanceof StatisticsKey) || obj == null) {
311 return false;
312 } else {
313 StatisticsKey other = (StatisticsKey) obj;
314 return (other.process.equals(this.process)) &&
315 (other.type.equals(this.type));
316 }
317 }
318 public int hashCode() {
319 return 7 + 11 * process.hashCode() + 17 * type.hashCode();
320 }
321 public String getType() {
322 return type;
323 }
324 public String getProcess() {
325 return process;
326 }
327 }
328
329 }