Checked in a much-improved benchmarking system; thanks to Ryan Gustafson for the excellent patch!

git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/trunk@4929 51baf565-9d33-0410-a72c-fc3788e3496d
This commit is contained in:
Tom Copeland
2007-01-09 12:00:14 +00:00
parent 4070fe0569
commit d4d8d5be61
6 changed files with 225 additions and 3 deletions

View File

@ -34,10 +34,11 @@ public class CommandLineOptions {
private String linePrefix;
private String linkPrefix;
private int minPriority = Rule.LOWEST_PRIORITY;
private boolean benchmark;
private boolean checkJavaFiles = true;
private boolean checkJspFiles = false;
private boolean checkJspFiles;
private String[] args;
@ -94,6 +95,8 @@ public class CommandLineOptions {
}
} else if (args[i].equals("-reportfile")) {
reportFile = args[++i];
} else if (args[i].equals("-benchmark")) {
benchmark = true;
}
}
}
@ -178,6 +181,10 @@ public class CommandLineOptions {
public int getMinPriority() {
return minPriority;
}
public boolean benchmark() {
return benchmark;
}
public String usage() {
return PMD.EOL + PMD.EOL +
@ -199,7 +206,10 @@ public class CommandLineOptions {
"-linkprefix: path to HTML source, for summary html renderer only" + PMD.EOL +
"-lineprefix: custom anchor to affected line in the source file, for summary html renderer only" + PMD.EOL +
"-minimumpriority: rule priority threshold; rules with lower priority than they will not be used" + PMD.EOL +
"-nojava: do not check Java files; default to check Java files" + PMD.EOL +
"-jsp: check JSP/JSF files; default to do not check JSP/JSF files" + PMD.EOL +
"-reportfile: send report output to a file; default to System.out" + PMD.EOL +
"-benchmark: output a benchmark report upon completion; default to System.err" + PMD.EOL +
PMD.EOL +
"For example: " + PMD.EOL +
"c:\\> java -jar pmd-" + PMD.VERSION + ".jar c:\\my\\source\\code text unusedcode,imports -targetjdk 1.5 -debug" + PMD.EOL +

View File

@ -10,6 +10,7 @@ import net.sourceforge.pmd.parsers.Parser;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.sourcetypehandlers.SourceTypeHandler;
import net.sourceforge.pmd.sourcetypehandlers.SourceTypeHandlerBroker;
import net.sourceforge.pmd.util.Benchmark;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
@ -81,19 +82,31 @@ public class PMD {
ctx.setSourceType(sourceType);
Parser parser = sourceTypeHandler.getParser();
parser.setExcludeMarker(excludeMarker);
long start = Benchmark.nanoTime();
Object rootNode = parser.parse(reader);
ctx.excludeLines(parser.getExcludeMap());
long end = Benchmark.nanoTime();
Benchmark.mark(Benchmark.TYPE_PARSER, "Parser", end - start, 0);
Thread.yield();
start = Benchmark.nanoTime();
sourceTypeHandler.getSymbolFacade().start(rootNode);
end = Benchmark.nanoTime();
Benchmark.mark(Benchmark.TYPE_SYMBOL_TABLE, "Symbol Table", end - start, 0);
Language language = SourceTypeToRuleLanguageMapper.getMappedLanguage(sourceType);
if (ruleSets.usesDFA(language)) {
start = Benchmark.nanoTime();
sourceTypeHandler.getDataFlowFacade().start(rootNode);
end = Benchmark.nanoTime();
Benchmark.mark(Benchmark.TYPE_DFA, "Data Flow Analysis", end - start, 0);
}
if (ruleSets.usesTypeResolution(language)) {
start = Benchmark.nanoTime();
sourceTypeHandler.getTypeResolutionFacade().start(rootNode);
end = Benchmark.nanoTime();
Benchmark.mark(Benchmark.TYPE_TYPE_RESOLUTION, "Type Resolution", end - start, 0);
}
List acus = new ArrayList();
@ -222,8 +235,10 @@ public class PMD {
}
public static void main(String[] args) {
long start = Benchmark.nanoTime();
CommandLineOptions opts = new CommandLineOptions(args);
long startFiles = Benchmark.nanoTime();
SourceFileSelector fileSelector = new SourceFileSelector();
fileSelector.setSelectJavaFiles(opts.isCheckJavaFiles());
@ -236,6 +251,8 @@ public class PMD {
} else {
files = collectFilesFromOneName(opts.getInputPath(), fileSelector);
}
long endFiles = Benchmark.nanoTime();
Benchmark.mark(Benchmark.TYPE_COLLECT_FILES, "Collect Files", endFiles - startFiles, 0);
SourceType sourceType;
if (opts.getTargetJDK().equals("1.3")) {
@ -262,11 +279,14 @@ public class PMD {
report.start();
try {
long startLoadRules = Benchmark.nanoTime();
RuleSetFactory ruleSetFactory = new RuleSetFactory();
ruleSetFactory.setMinimumPriority(opts.getMinPriority());
RuleSets rulesets = ruleSetFactory.createRuleSets(opts.getRulesets());
printRuleNamesInDebug(opts.debugEnabled(), rulesets);
long endLoadRules = Benchmark.nanoTime();
Benchmark.mark(Benchmark.TYPE_LOAD_RULES, "Load Rules", endLoadRules - startLoadRules, 0);
processFiles(opts.getCpus(), ruleSetFactory, sourceType, files, ctx,
opts.getRulesets(), opts.debugEnabled(), opts.shortNamesEnabled(),
@ -278,6 +298,7 @@ public class PMD {
report.end();
Writer w = null;
long reportStart = Benchmark.nanoTime();
try {
Renderer r = opts.createRenderer();
if (opts.getReportFile() != null) {
@ -290,6 +311,7 @@ public class PMD {
w.flush();
if (opts.getReportFile() != null) {
w.close();
w = null;
}
} catch (Exception e) {
System.out.println(e.getMessage());
@ -305,6 +327,14 @@ public class PMD {
System.out.println(e.getMessage());
}
}
long reportEnd = Benchmark.nanoTime();
Benchmark.mark(Benchmark.TYPE_REPORTING, "Reporting", reportEnd - reportStart, 0);
}
if (opts.benchmark()) {
long end = Benchmark.nanoTime();
Benchmark.mark(Benchmark.TYPE_TOTAL, "Total PMD", end - start, 0);
System.err.println(Benchmark.report());
}
}

View File

@ -8,6 +8,8 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import net.sourceforge.pmd.util.Benchmark;
/**
* This class represents a collection of rules.
@ -92,9 +94,13 @@ public class RuleSet {
public void apply(List acuList, RuleContext ctx) {
Iterator rs = rules.iterator();
long start = Benchmark.nanoTime();
while (rs.hasNext()) {
Rule rule = (Rule) rs.next();
rule.apply(acuList, ctx);
long end = Benchmark.nanoTime();
Benchmark.mark(Benchmark.TYPE_RULE, rule.getName(), end - start, 1);
start = end;
}
}

View File

@ -23,9 +23,15 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
@ -169,4 +175,154 @@ public class Benchmark {
if (debug) System.out.println("Done timing " + rule.getName() + "; elapsed time was " + elapsed);
}
}
private static final Map nameToBenchmarkResult = new HashMap();
public static final int TYPE_RULE = 0;
public static final int TYPE_RULE_CHAIN_RULE = 1;
public static final int TYPE_COLLECT_FILES = 2;
public static final int TYPE_LOAD_RULES = 3;
public static final int TYPE_PARSER = 4;
public static final int TYPE_SYMBOL_TABLE = 5;
public static final int TYPE_DFA = 6;
public static final int TYPE_TYPE_RESOLUTION = 7;
public static final int TYPE_RULE_CHAIN = 8;
public static final int TYPE_REPORTING = 9;
private static final int TYPE_RULE_TOTAL = 10;
private static final int TYPE_RULE_CHAIN_RULE_TOTAL = 11;
private static final int TYPE_MEASURED_TOTAL = 12;
private static final int TYPE_UNMEASURED_TOTAL = 13;
public static final int TYPE_TOTAL = 14;
private static final class BenchmarkResult implements Comparable {
private final int type;
private final String name;
private long time;
private long count;
public BenchmarkResult(int type, String name) {
this.type = type;
this.name = name;
}
public BenchmarkResult(int type, String name, long time, long count) {
this.type = type;
this.name = name;
this.time = time;
this.count = count;
}
public int getType() {
return type;
}
public String getName() {
return name;
}
public long getTime() {
return time;
}
public long getCount() {
return count;
}
public void update(long time, long count) {
this.time += time;
this.count += count;
}
public int compareTo(Object o) {
BenchmarkResult benchmarkResult = (BenchmarkResult)o;
int cmp = this.type - benchmarkResult.type;
if (cmp == 0) {
long delta = this.time - benchmarkResult.time;
cmp = delta > 0 ? 1 : (delta < 0 ? -1 : 0);
}
return cmp;
}
}
public static long nanoTime() {
return System.currentTimeMillis() * 1000000;
//return System.nanoTime();
}
public synchronized static void mark(int type, String name, long time, long count) {
BenchmarkResult benchmarkResult = (BenchmarkResult)nameToBenchmarkResult.get(name);
if (benchmarkResult == null) {
benchmarkResult = new BenchmarkResult(type, name);
nameToBenchmarkResult.put(name, benchmarkResult);
}
benchmarkResult.update(time, count);
}
public static void reset() {
nameToBenchmarkResult.clear();
}
public static String report() {
List results = new ArrayList(nameToBenchmarkResult.values());
long totalTime[] = new long[TYPE_TOTAL + 1];
long totalCount[] = new long[TYPE_TOTAL + 1];
for (Iterator i = results.iterator(); i.hasNext();) {
BenchmarkResult benchmarkResult = (BenchmarkResult)i.next();
totalTime[benchmarkResult.getType()] += benchmarkResult.getTime();
totalCount[benchmarkResult.getType()] += benchmarkResult.getCount();
if (benchmarkResult.getType() < TYPE_MEASURED_TOTAL) {
totalTime[TYPE_MEASURED_TOTAL] += benchmarkResult.getTime();
}
}
results.add(new BenchmarkResult(TYPE_RULE_TOTAL, "Rule Total", totalTime[TYPE_RULE], 0));
results.add(new BenchmarkResult(TYPE_RULE_CHAIN_RULE_TOTAL, "RuleChain Rule Total", totalTime[TYPE_RULE_CHAIN_RULE], 0));
results.add(new BenchmarkResult(TYPE_MEASURED_TOTAL, "Measured", totalTime[TYPE_MEASURED_TOTAL], 0));
results.add(new BenchmarkResult(TYPE_UNMEASURED_TOTAL, "Non-measured", totalTime[TYPE_TOTAL] - totalTime[TYPE_MEASURED_TOTAL], 0));
Collections.sort(results);
StringBuffer buf = new StringBuffer();
boolean writeRuleHeader = true;
boolean writeRuleChainRuleHeader = true;
for (Iterator i = results.iterator(); i.hasNext();) {
BenchmarkResult benchmarkResult = (BenchmarkResult)i.next();
StringBuffer buf2 = new StringBuffer();
buf2.append(benchmarkResult.getName());
buf2.append(":");
while (buf2.length() <= 50) {
buf2.append(" ");
}
buf2.append(StringUtil.lpad(MessageFormat.format("{0,number,0.00000}", new Object[]{new Double(benchmarkResult.getTime()/1000000000.0)}), 8, " "));
if (benchmarkResult.getType() <= TYPE_RULE_CHAIN_RULE) {
buf2.append(StringUtil.lpad(MessageFormat.format("{0,number,###,###,###,###,###}", new Object[]{new Long(benchmarkResult.getCount())}), 20, " "));
}
switch (benchmarkResult.getType()) {
case TYPE_RULE:
if (writeRuleHeader) {
writeRuleHeader = false;
buf.append(PMD.EOL);
buf.append("---------------------------------<<< Rules >>>---------------------------------" + PMD.EOL);
buf.append("Rule name Time (secs) # of Evaluations" + PMD.EOL);
buf.append(PMD.EOL);
}
break;
case TYPE_RULE_CHAIN_RULE:
if (writeRuleChainRuleHeader) {
writeRuleChainRuleHeader = false;
buf.append(PMD.EOL);
buf.append("----------------------------<<< RuleChain Rules >>>----------------------------" + PMD.EOL);
buf.append("Rule name Time (secs) # of Visits" + PMD.EOL);
buf.append(PMD.EOL);
}
break;
case TYPE_COLLECT_FILES:
buf.append(PMD.EOL);
buf.append("--------------------------------<<< Summary >>>--------------------------------" + PMD.EOL);
buf.append("Segment Time (secs)" + PMD.EOL);
buf.append(PMD.EOL);
break;
case TYPE_MEASURED_TOTAL:
buf.append(PMD.EOL);
buf.append("-----------------------------<<< Final Summary >>>-----------------------------" + PMD.EOL);
buf.append("Total Time (secs)" + PMD.EOL);
buf.append(PMD.EOL);
break;
}
buf.append(buf2.toString());
buf.append(PMD.EOL);
}
return buf.toString();
}
}

View File

@ -257,5 +257,25 @@ public class StringUtil {
results[i] = strings[i].substring(trimDepth);
}
return results;
}
}
/**
* Left pads a string.
* @param s The String to pad
* @param length The desired minimum length of the resulting padded String
* @param pad The String to pad with
* @return The resulting left padded String
*/
public static String lpad(String s, int length, String pad) {
if (pad == null || pad.length() == 0) {
pad = " ";
}
StringBuffer buf = new StringBuffer(length);
for (int i = 0; i < length - s.length();) {
buf.append(pad);
i+= pad.length();
}
buf.append(s);
return buf.toString();
}
}

View File

@ -54,7 +54,7 @@
</subsection>
<subsection name="Contributors">
<ul>
<li>Ryan Gustafson - Patch to add more annotation suppression tests, patch to fix bug in AvoidDecimalLiteralsInBigDecimalConstructor, patch to add "ref" overrides to RuleSetFactory, patch to fix JDK 1.3 incompatibilities in PMD 2.0, patch to support classpaths with spaces in pmd.bat, patch to fix controversial/DefaultPackage XPath rule</li>
<li>Ryan Gustafson - Better benchmarking code, patch to add more annotation suppression tests, patch to fix bug in AvoidDecimalLiteralsInBigDecimalConstructor, patch to add "ref" overrides to RuleSetFactory, patch to fix JDK 1.3 incompatibilities in PMD 2.0, patch to support classpaths with spaces in pmd.bat, patch to fix controversial/DefaultPackage XPath rule</li>
<li>Lukas Theussl - Patch to bring Maven configuration files up to date</li>
<li>Jason Bennett - Rewrite of annotation-based warning suppression to allow for rule-specific suppression, noticed useless line in XSLT scripts, fix for UnnecessaryLocalBeforeReturn, wrote NPathComplexity rule, patches to improve CyclomaticComplexity rule, Implemented: UseCollectionIsEmpty, NcssTypeCount, NcssMethodCount, NcssConstructor, Patch to detect comparison with new Object</li>
<li>Brent Fisher - Fixed report backslash bug, SummaryHTML report improvements</li>