Benchmark.java
001 package net.sourceforge.pmd.util;
002 
003 import java.io.IOException;
004 import java.io.InputStreamReader;
005 import java.io.Reader;
006 import java.text.MessageFormat;
007 import java.util.ArrayList;
008 import java.util.Collection;
009 import java.util.Collections;
010 import java.util.HashMap;
011 import java.util.Iterator;
012 import java.util.List;
013 import java.util.Map;
014 import java.util.Set;
015 import java.util.TreeSet;
016 
017 import net.sourceforge.pmd.PMD;
018 import net.sourceforge.pmd.PMDException;
019 import net.sourceforge.pmd.Rule;
020 import net.sourceforge.pmd.RuleContext;
021 import net.sourceforge.pmd.RuleSet;
022 import net.sourceforge.pmd.RuleSetFactory;
023 import net.sourceforge.pmd.RuleSetNotFoundException;
024 import net.sourceforge.pmd.RuleSets;
025 import net.sourceforge.pmd.lang.Language;
026 import net.sourceforge.pmd.lang.LanguageFilenameFilter;
027 import net.sourceforge.pmd.lang.LanguageVersion;
028 import net.sourceforge.pmd.lang.LanguageVersionHandler;
029 import net.sourceforge.pmd.util.datasource.DataSource;
030 
031 public class Benchmark {
032 
033     private static class Result implements Comparable<Result> {
034         public Rule rule;
035         public long time;
036 
037         public int compareTo(Result other) {
038             if (other.time < time) {
039                 return -1;
040             else if (other.time > time) {
041                 return 1;
042             }
043 
044             return rule.getName().compareTo(other.rule.getName());
045         }
046 
047         public Result(long elapsed, Rule rule) {
048             this.rule = rule;
049             this.time = elapsed;
050         }
051     }
052 
053     private static boolean findBooleanSwitch(String[] args, String name) {
054         for (int i = 0; i < args.length; i++) {
055             if (args[i].equals(name)) {
056                 return true;
057             }
058         }
059         return false;
060     }
061 
062     private static String findOptionalStringValue(String[] args, String name, String defaultValue) {
063         for (int i = 0; i < args.length; i++) {
064             if (args[i].equals(name)) {
065                 return args[i + 1];
066             }
067         }
068         return defaultValue;
069     }
070 
071     public static void main(String[] argsthrows RuleSetNotFoundException, IOException, PMDException {
072 
073         String targetjdk = findOptionalStringValue(args, "--targetjdk""1.4");
074         Language language = Language.JAVA;
075         LanguageVersion languageVersion = language.getVersion(targetjdk);
076         if (languageVersion == null) {
077           languageVersion = language.getDefaultVersion();
078         }
079 
080         String srcDir = findOptionalStringValue(args, "--source-directory""/usr/local/java/src/java/lang/");
081         List<DataSource> dataSources = FileUtil.collectFiles(srcDir, new LanguageFilenameFilter(language));
082 
083         boolean debug = findBooleanSwitch(args, "--debug");
084         boolean parseOnly = findBooleanSwitch(args, "--parse-only");
085 
086         if (debug) {
087             System.out.println("Using " +language.getName() " " + languageVersion.getVersion());
088         }
089         if (parseOnly) {
090             parseStress(languageVersion, dataSources);
091         else {
092             String ruleset = findOptionalStringValue(args, "--ruleset""");
093             if (debug) {
094           System.out.println("Checking directory " + srcDir);
095             }
096             Set<Result> results = new TreeSet<Result>();
097             RuleSetFactory factory = new RuleSetFactory();
098             if (ruleset.length() 0) {
099                 stress(languageVersion, factory.createRuleSet(ruleset), dataSources, results, debug);
100             else {
101                 Iterator<RuleSet> i = factory.getRegisteredRuleSets();
102                 while (i.hasNext()) {
103                     stress(languageVersion, i.next(), dataSources, results, debug);
104                 }
105             }
106             System.out.println("=========================================================");
107             System.out.println("Rule\t\t\t\t\t\tTime in ms");
108             System.out.println("=========================================================");
109             for (Result result: results) {
110                 StringBuffer out = new StringBuffer(result.rule.getName());
111                 while (out.length() 48) {
112                     out.append(' ');
113                 }
114                 out.append(result.time);
115                 System.out.println(out.toString());
116             }
117         }
118 
119         System.out.println("=========================================================");
120     }
121 
122     private static void parseStress(LanguageVersion languageVersion, List<DataSource> dataSourcesthrows IOException {
123         long start = System.currentTimeMillis();
124         for (DataSource dataSource: dataSources) {
125             LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler();
126             languageVersionHandler.getParser().parse(dataSource.getNiceFileName(false, null)new InputStreamReader(dataSource.getInputStream()));
127         }
128         long end = System.currentTimeMillis();
129         long elapsed = end - start;
130         System.out.println("That took " + elapsed + " ms");
131     }
132 
133     private static void stress(LanguageVersion languageVersion, RuleSet ruleSet, List<DataSource> dataSources, Set<Result> results, boolean debugthrows PMDException, IOException {
134         Collection<Rule> rules = ruleSet.getRules();
135         for (Rule rule: rules) {
136             if (debug) {
137           System.out.println("Starting " + rule.getName());
138             }
139 
140             RuleSet working = new RuleSet();
141             working.addRule(rule);
142             RuleSets ruleSets = new RuleSets();
143             ruleSets.addRuleSet(working);
144 
145             PMD p = new PMD();
146             p.getConfiguration().setDefaultLanguageVersion(languageVersion);
147             RuleContext ctx = new RuleContext();
148             long start = System.currentTimeMillis();
149             for (DataSource dataSource: dataSources) {
150                 Reader reader = new InputStreamReader(dataSource.getInputStream());
151                 ctx.setSourceCodeFilename(dataSource.getNiceFileName(false, null));
152                 p.processFile(reader, ruleSets, ctx);
153                 reader.close();
154             }
155             long end = System.currentTimeMillis();
156             long elapsed = end - start;
157             results.add(new Result(elapsed, rule));
158             if (debug) {
159           System.out.println("Done timing " + rule.getName() "; elapsed time was " + elapsed);
160             }
161         }
162     }
163 
164     private static final Map<String, BenchmarkResult> NAME_TO_BENCHMARK_RESULT = new HashMap<String, BenchmarkResult>();
165 
166     public static final int TYPE_RULE = 0;
167     public static final int TYPE_RULE_CHAIN_RULE = 1;
168     public static final int TYPE_COLLECT_FILES = 2;
169     public static final int TYPE_LOAD_RULES = 3;
170     public static final int TYPE_PARSER = 4;
171     public static final int TYPE_SYMBOL_TABLE = 5;
172     public static final int TYPE_DFA = 6;
173     public static final int TYPE_TYPE_RESOLUTION = 7;
174     public static final int TYPE_RULE_CHAIN_VISIT = 8;
175     public static final int TYPE_REPORTING = 9;
176     private static final int TYPE_RULE_TOTAL = 10;
177     private static final int TYPE_RULE_CHAIN_RULE_TOTAL = 11;
178     private static final int TYPE_MEASURED_TOTAL = 12;
179     private static final int TYPE_NON_MEASURED_TOTAL = 13;
180     public static final int TYPE_TOTAL_PMD = 14;
181     
182     private static final String[] TYPE_NAMES = {
183         null,
184         null,
185         "Collect Files",
186         "Load Rules",
187         "Parser",
188         "Symbol Table",
189         "Data Flow Analysis",
190         "Type Resolution",
191         "RuleChain Visit",
192         "Reporting",
193         "Rule Total",
194         "RuleChain Rule Total",
195         "Measured",
196         "Non-measured",
197         "Total PMD",
198     };
199 
200     private static final class BenchmarkResult implements Comparable<BenchmarkResult> {
201         private final int type;
202         private final String name;
203         private long time;
204         private long count;
205         public BenchmarkResult(int type, String name) {
206             this.type = type;
207             this.name = name;
208         }
209         public BenchmarkResult(int type, String name, long time, long count) {
210             this.type = type;
211             this.name = name;
212             this.time = time;
213             this.count = count;
214         }
215         public int getType() {
216             return type;
217         }
218         public String getName() {
219             return name;
220         }
221         public long getTime() {
222             return time;
223         }
224         public long getCount() {
225             return count;
226         }
227         public void update(long time, long count) {
228             this.time += time;
229             this.count += count;
230         }
231         public int compareTo(BenchmarkResult benchmarkResult) {
232             int cmp = this.type - benchmarkResult.type;
233             if (cmp == 0) {
234                 long delta = this.time - benchmarkResult.time;
235                 cmp = delta > (delta < ? -0);
236             }
237             return cmp;
238         }
239     }
240 
241     public static void mark(int type, long time, long count) {
242         mark(type, null, time, count);
243     }
244 
245     public synchronized static void mark(int type, String name, long time, long count) {
246         String typeName = TYPE_NAMES[type];
247         if (typeName != null && name != null) {
248             throw new IllegalArgumentException("Name cannot be given for type: " + type);
249         else if (typeName == null && name == null) {
250             throw new IllegalArgumentException("Name is required for type: " + type);
251         else if (typeName == null) {
252             typeName = name;
253         }
254         BenchmarkResult benchmarkResult = NAME_TO_BENCHMARK_RESULT.get(typeName);
255         if (benchmarkResult == null) {
256             benchmarkResult = new BenchmarkResult(type, typeName);
257             NAME_TO_BENCHMARK_RESULT.put(typeName, benchmarkResult);
258         }
259         benchmarkResult.update(time, count);
260     }
261 
262     public static void reset() {
263         NAME_TO_BENCHMARK_RESULT.clear();
264     }
265 
266     public static String report() {
267         List<BenchmarkResult> results = new ArrayList<BenchmarkResult>(NAME_TO_BENCHMARK_RESULT.values());
268 
269         long totalTime[] new long[TYPE_TOTAL_PMD + 1];
270         long totalCount[] new long[TYPE_TOTAL_PMD + 1];
271         for (BenchmarkResult benchmarkResult: results) {
272             totalTime[benchmarkResult.getType()] += benchmarkResult.getTime();
273             totalCount[benchmarkResult.getType()] += benchmarkResult.getCount();
274             if (benchmarkResult.getType() < TYPE_MEASURED_TOTAL) {
275                 totalTime[TYPE_MEASURED_TOTAL+= benchmarkResult.getTime();
276             }
277         }
278         results.add(new BenchmarkResult(TYPE_RULE_TOTAL, TYPE_NAMES[TYPE_RULE_TOTAL], totalTime[TYPE_RULE]0));
279         results.add(new BenchmarkResult(TYPE_RULE_CHAIN_RULE_TOTAL, TYPE_NAMES[TYPE_RULE_CHAIN_RULE_TOTAL], totalTime[TYPE_RULE_CHAIN_RULE]0));
280         results.add(new BenchmarkResult(TYPE_MEASURED_TOTAL, TYPE_NAMES[TYPE_MEASURED_TOTAL], totalTime[TYPE_MEASURED_TOTAL]0));
281         results.add(new BenchmarkResult(TYPE_NON_MEASURED_TOTAL, TYPE_NAMES[TYPE_NON_MEASURED_TOTAL], totalTime[TYPE_TOTAL_PMD- totalTime[TYPE_MEASURED_TOTAL]0));
282         Collections.sort(results);
283 
284         StringBuffer buf = new StringBuffer();
285         boolean writeRuleHeader = true;
286         boolean writeRuleChainRuleHeader = true;
287         long ruleCount = 0;
288         long ruleChainCount = 0;
289         for (BenchmarkResult benchmarkResult: results) {
290             StringBuffer buf2 = new StringBuffer();
291             buf2.append(benchmarkResult.getName());
292             buf2.append(':');
293             while (buf2.length() <= 50) {
294                 buf2.append(' ');
295             }
296             buf2.append(StringUtil.lpad(MessageFormat.format("{0,number,0.000}", Double.valueOf(benchmarkResult.getTime()/1000000000.0))8));
297             if (benchmarkResult.getType() <= TYPE_RULE_CHAIN_RULE) {
298                 buf2.append(StringUtil.lpad(MessageFormat.format("{0,number,###,###,###,###,###}", benchmarkResult.getCount())20));
299             }
300             switch (benchmarkResult.getType()) {
301                 case TYPE_RULE:
302                     if (writeRuleHeader) {
303                         writeRuleHeader = false;
304                         buf.append(PMD.EOL);
305                         buf.append("---------------------------------<<< Rules >>>---------------------------------" + PMD.EOL);
306                         buf.append("Rule name                                       Time (secs)    # of Evaluations" + PMD.EOL);
307                         buf.append(PMD.EOL);
308                     }
309                     ruleCount++;
310                     break;
311                 case TYPE_RULE_CHAIN_RULE:
312                     if (writeRuleChainRuleHeader) {
313                         writeRuleChainRuleHeader = false;
314                         buf.append(PMD.EOL);
315                         buf.append("----------------------------<<< RuleChain Rules >>>----------------------------" + PMD.EOL);
316                         buf.append("Rule name                                       Time (secs)         # of Visits" + PMD.EOL);
317                         buf.append(PMD.EOL);
318                     }
319                     ruleChainCount++;
320                     break;
321                 case TYPE_COLLECT_FILES:
322                     buf.append(PMD.EOL);
323                     buf.append("--------------------------------<<< Summary >>>--------------------------------" + PMD.EOL);
324                     buf.append("Segment                                         Time (secs)" + PMD.EOL);
325                     buf.append(PMD.EOL);
326                     break;
327                 case TYPE_MEASURED_TOTAL:
328                     String s = MessageFormat.format("{0,number,###,###,###,###,###}", ruleCount);
329                     buf.append("Rule Average (" + s + " rules):" + StringUtil.lpad(MessageFormat.format("{0,number,0.000}", ruleCount==0?0:totalTime[TYPE_RULE]/1000000000.0d/ruleCount)37-s.length()) + PMD.EOL);
330                     s = MessageFormat.format("{0,number,###,###,###,###,###}", ruleChainCount);
331                     buf.append("RuleChain Average (" + s + " rules):" + StringUtil.lpad(MessageFormat.format("{0,number,0.000}", ruleChainCount==0?0:totalTime[TYPE_RULE_CHAIN_RULE]/1000000000.0d/ruleChainCount)32-s.length()) + PMD.EOL);
332 
333                     buf.append(PMD.EOL);
334                     buf.append("-----------------------------<<< Final Summary >>>-----------------------------" + PMD.EOL);
335                     buf.append("Total                                           Time (secs)" + PMD.EOL);
336                     buf.append(PMD.EOL);
337                     break;
338                 default:
339                     // Do nothing
340                     break;
341             }
342             buf.append(buf2.toString());
343             buf.append(PMD.EOL);
344         }
345         return buf.toString();
346     }
347 }