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