JDK-6827648 : Extremely slow compilation time for visitor pattern code + generics
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6u13
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2009-04-08
  • Updated: 2012-01-14
  • Resolved: 2009-08-14
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b70Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
I have tried this using JDK 1.6 Update 13 on Windows x64, and the
problem persists.

A DESCRIPTION OF THE REQUEST :

Visitor pattern code which uses generics takes an unfeasibly long time (>30 seconds) to compile a single source file for a visitor.

The case causing difficulties appears to be:
(1) Genericised interface containing lots of methods all called "visit", all with similar but different signatures.
(2) Abstract generic class implementing (1) with all methods implemented
(3) Concrete non-generic class extending (2)

Compiling (3) takes an extremely long time. See the attached source code which generates Java code to be run through javac.


JUSTIFICATION :
The JTB (Java Tree Builder) extension to javacc uses the Visitor pattern extensively, and our solution uses over 40 different visitors. This increases the compile time of the whole project to about 25 minutes, whereas similar sized projects compile in under a minute.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Takes under a second to compile a single source file (extending AbstractClass<Object>) which contains no methods of its own.
ACTUAL -
Takes over 30 seconds to compile a single source file (extending AbstractClass<Object>) which contains no methods of its own.

---------- BEGIN SOURCE ----------
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;


public class CodeGenerator
{
	private final static String PATH = "D:\\tmp\\javacprob\\2";
	private final static int NUM_TYPES = 1000;
	
	public static void main(String[] args) throws FileNotFoundException
    {
		PrintStream interfacePs = new PrintStream(PATH + File.separator + "Interface.java");
		PrintStream abstractClassPs = new PrintStream(PATH + File.separator + "AbstractClass.java");
		PrintStream implementingClassPs = new PrintStream(PATH + File.separator + "ImplementingClass.java");
		interfacePs.println("public interface Interface<T> {");
		abstractClassPs.println("public abstract class AbstractClass<T> implements Interface<T> {");
		implementingClassPs.println("public class ImplementingClass extends AbstractClass<Object> {");
		
		for (int i=0; i<NUM_TYPES; i++)
		{
			String nodeName = "Node" + i;
			PrintStream nodePs = new PrintStream(PATH + File.separator + nodeName + ".java");
			nodePs.printf("public class %s { }\n", nodeName);
			nodePs.close();
			interfacePs.printf("void visit(%s node, T obj);%n", nodeName);
			abstractClassPs.printf("public void visit(%s node, T obj) { System.out.println(obj.toString()); }%n", nodeName);
		}
		interfacePs.println("}");
		abstractClassPs.println("}");
		implementingClassPs.println("}");
		interfacePs.close();
		abstractClassPs.close();
		implementingClassPs.close();
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The only workaround we have found is to use an alternative compiler.

Comments
SUGGESTED FIX A webrev of this fix is available at the following URL: http://hg.openjdk.java.net/jdk7/tl/langtools/rev/85fecace920b
20-07-2009

EVALUATION After some analysis with the NB profiler it seems like javac is spending most of the time in order to (i) determine membership (that is, given a symbol and a type corresponding to a site, instantiate the symbol as a member of the site) and (ii) implementation checking (that is, given an origin and a method symbol m, find the method that implements m in origin). Both problems are addressable by adding some form of caching inside the compiler, to avoid multiple recalculations of types/symbols. The prototype with the changes described in 'suggested fix' manages to compile the test case in about 5 seconds.
20-07-2009