View Javadoc

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 org.apache.commons.math.stat.descriptive.SummaryStatistics;
24  
25  /**
26   * <p>Container for {@link SummaryStatistics} accumulated during 
27   * {@link ClientThread} executions.</p>
28   * 
29   * <p>Maintains a HashMap of {@link SummaryStatistics} instances with a composite
30   * key of the form (process,type).  "Process" typically identifies the client
31   * thread and "type" identifies the metric - e.g., "latency", "numActive."</p>
32   * 
33   * <p>{@link ClientThread#run()} adds one <code>SummaryStatistics</code>
34   * instance, with key = (current thread id,"latency").</p>
35   *
36   */
37  public class Statistics {
38      private HashMap<StatisticsKey,SummaryStatistics> data = 
39          new HashMap <StatisticsKey,SummaryStatistics>();
40      
41      /**
42       * Adds the results of the given SummaryStatistics instance under
43       * the key <process,type>
44       * 
45       * @param stats the SummaryStatistics whose results we are adding
46       * @param process name of the associated process
47       * @param type description of the associated metric
48       */
49      public synchronized void addStatistics(SummaryStatistics stats,
50              String process, String type) {
51          StatisticsKey key = new StatisticsKey(process, type);
52          data.put(key, stats);
53      }
54      
55      /**
56       * Retrieves the SummaryStatistics corresponding to the given
57       * process and type, if this exists; null otherwise.
58       * 
59       * @param process name of the associated process
60       * @param type description of the associated metric
61       * @return SummaryStatistics for the given <process,type>; null if there is
62       * no such element in the container
63       */
64      public synchronized SummaryStatistics getStatistics(String process,
65              String type) {
66          StatisticsKey key = new StatisticsKey(process, type);
67          return data.get(key);
68      }
69      
70      /**
71       * Returns the full list of SummaryStatistics corresponding to
72       * the given <code>type</code> - i.e, the list of statistics of the 
73       * given type across processes.  For example, 
74       * <code>getStatisticsByType("latency")</code> will return a list of latency
75       * summaries, one for each process, assuming "latency" is the name of 
76       * an accumulated metric.
77       * 
78       * @param type the type value to get statistics for
79       * @return the List of SummaryStatistics stored under the given type
80       */
81      public synchronized List<SummaryStatistics> getStatisticsByType(String type) {
82          ArrayList<SummaryStatistics> result = new ArrayList<SummaryStatistics>();
83          Iterator<StatisticsKey> it = data.keySet().iterator();
84          while (it.hasNext()) {
85              StatisticsKey key = it.next();
86              if (key.type.equals(type)) {
87                  result.add(data.get(key));
88              }
89          }
90          return result;
91      }
92      
93      /**
94       * Returns the full list of SummaryStatistics corresponding to
95       * the given <code>process</code> - i.e, the list of statistics of
96       * of different types maintained for the given process. 
97       * 
98       * @param type the type value to get statistics for
99       * @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 }