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 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 }