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 }