1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.performance;
18
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.io.Serializable;
24 import org.apache.commons.math.stat.descriptive.SummaryStatistics;
25
26 /**
27 * <p>Container for {@link SummaryStatistics} accumulated during
28 * {@link ClientThread} executions.</p>
29 *
30 * <p>Maintains a HashMap of {@link SummaryStatistics} instances with a composite
31 * key of the form (process,type). "Process" typically identifies the client
32 * thread and "type" identifies the metric - e.g., "latency", "numActive."</p>
33 *
34 * <p>{@link ClientThread#run()} adds one <code>SummaryStatistics</code>
35 * instance, with key = (current thread id,"latency").</p>
36 *
37 */
38 public class Statistics implements Serializable {
39 private HashMap<StatisticsKey,SummaryStatistics> data =
40 new HashMap <StatisticsKey,SummaryStatistics>();
41
42 /**
43 * Adds the results of the given SummaryStatistics instance under
44 * the key <process,type>
45 *
46 * @param stats the SummaryStatistics whose results we are adding
47 * @param process name of the associated process
48 * @param type description of the associated metric
49 */
50 public synchronized void addStatistics(SummaryStatistics stats,
51 String process, String type) {
52 StatisticsKey key = new StatisticsKey(process, type);
53 data.put(key, stats);
54 }
55
56 /**
57 * Retrieves the SummaryStatistics corresponding to the given
58 * process and type, if this exists; null otherwise.
59 *
60 * @param process name of the associated process
61 * @param type description of the associated metric
62 * @return SummaryStatistics for the given <process,type>; null if there is
63 * no such element in the container
64 */
65 public synchronized SummaryStatistics getStatistics(String process,
66 String type) {
67 StatisticsKey key = new StatisticsKey(process, type);
68 return data.get(key);
69 }
70
71 /**
72 * Returns the full list of SummaryStatistics corresponding to
73 * the given <code>type</code> - i.e, the list of statistics of the
74 * given type across processes. For example,
75 * <code>getStatisticsByType("latency")</code> will return a list of latency
76 * summaries, one for each process, assuming "latency" is the name of
77 * an accumulated metric.
78 *
79 * @param type the type value to get statistics for
80 * @return the List of SummaryStatistics stored under the given type
81 */
82 public synchronized List<SummaryStatistics> getStatisticsByType(String type) {
83 ArrayList<SummaryStatistics> result = new ArrayList<SummaryStatistics>();
84 Iterator<StatisticsKey> it = data.keySet().iterator();
85 while (it.hasNext()) {
86 StatisticsKey key = it.next();
87 if (key.type.equals(type)) {
88 result.add(data.get(key));
89 }
90 }
91 return result;
92 }
93
94 /**
95 * Returns the full list of SummaryStatistics corresponding to
96 * the given <code>process</code> - i.e, the list of statistics of
97 * of different types maintained for the given process.
98 *
99 * @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 }