I’ve already written a blog post on this topic : https://tcollignon.github.io/2016/12/04/How-to-compile-JSP-with-Tomcat-and-Maven-faster.html Meanwhile, I have proposed some PR to the project https://github.com/leonardehrenfried/jspc-maven-plugin, and now I’m an official committer. I use this plugin in my work every day to compile many JSP, and I always try to compile faster.

How to compile JSP

Like I said in my other blog post this is my current Maven configuration :

<plugin>
   <groupId>io.leonard.maven.plugins</groupId>
   <artifactId>jspc-maven-plugin</artifactId>
   <version>2.4.1</version>
   <configuration>
      <webAppSourceDirectory>${project.build.directory}/${project.artifactId}-${project.version}</webAppSourceDirectory>
      <webXml>${project.build.directory}/${project.artifactId}-${project.version}/WEB-INF/web.xml</webXml>
      <generatedClasses>${project.build.directory}/jspc</generatedClasses>
   </configuration>
   <dependencies>
      <dependency>
         <groupId>org.apache.tomcat</groupId>
         <artifactId>tomcat-jasper</artifactId>
         <version>8.5.4</version>
         <exclusions>
            <exclusion>
               <groupId>org.eclipse.jdt.core.compiler</groupId>
               <artifactId>ecj</artifactId>
            </exclusion>
         </exclusions>
      </dependency>
      <dependency>
         <groupId>org.eclipse.jdt.core.compiler</groupId>
         <artifactId>ecj</artifactId>
         <version>4.6.1</version>
      </dependency>
   </dependencies>
</plugin>

Note : I override tomcat-jasper in this configuration in order to use version 8.5.4 (by default it’s 8.5.8).

Giving more threads

Our project includes many jsp (> 3000), so the compilation take time. We can increase number of parallel threads. Usually I use 4 threads, but for now I want to improve this. So I test with 16 threads. But surprise, the global time is not better than with 4 threads. Let’s see what happen with jvisualvm :

threadsSynchronizedJDTCompiler.PNG

We can see that there are many synchronization phases for each threads. That is not good for global performance.

Trying to fix synchronization

I want to avoid these synchronization phases, so I take a look at the source code. The main problem is the code which call java.util.jar.jarFile, and it’s called by sun.misc.URL.classPath.getResource(String) In the compiler the methods which called that most of the time are org.apache.jasper.compiler.JDTCompiler$1.findType(String) and org.apache.jasper.compiler.JDTCompiler$1.isPackage(String) I try to rewrite part of it to totally avoid calling such a code, this is the case of org.apache.jasper.compiler.JDTCompiler$1.isPackage(String). But some part of code can’t be rewriting easily, so I decided to add a synchronized hash map to add cache mechanism. This is the case of org.apache.jasper.compiler.JDTCompiler$1.findType(String). With this 2 fixes I improve synchronized parts, jvisualvm confirms that :

threadsSynchronizedParallelJDTCompiler.PNG

It’s better ! We see that at the beginning there are a lot of syncrhonizations, it’s a normal behavior. It’s time to put results in the cache, and after that the cache will always be used, so there will be no more synchronization phase. But if we inspect the diagram in the last part we see that theres are other synchronizations. They are in the core of the compiler. I can’t easily add a cache map to this part of code. I think it will take’s too much time to fix this …​ So I don’t fix that, it’s still better than at the beginning.

Switch compiler class in Maven plugin

To use this improvement with Maven goal, I add a new option call "compilerClass" and I add a new Compiler in the plugin. So by default compilerClass=org.apache.jasper.compiler.JDTCompiler, and it’s working perfectly with 1 thread. But when we have more than 2 threads it’s better to use the new one "org.apache.jasper.compiler.ParallelJDTCompiler". This PR have been merged in version 2.4.3 of the plugin : https://github.com/leonardehrenfried/jspc-maven-plugin/releases/tag/jspc-maven-plugin-2.4.3

<plugin>
   <groupId>io.leonard.maven.plugins</groupId>
   <artifactId>jspc-maven-plugin</artifactId>
   <version>2.4.3</version>
   <configuration>
      <webAppSourceDirectory>${project.build.directory}/${project.artifactId}-${project.version}</webAppSourceDirectory>
      <webXml>${project.build.directory}/${project.artifactId}-${project.version}/WEB-INF/web.xml</webXml>
      <generatedClasses>${project.build.directory}/jspc</generatedClasses>
      <compilerClass>org.apache.jasper.compiler.ParallelJDTCompiler</compilerClass>
      <threads>4</threads>
   </configuration>
</plugin>

Benchmark

I made a little benchmark including one project with ~3000 jsp. I test this on HP ZBook i7 Windows 7.

Duration

2 threads JDTCompiler

2min28s

2 threads ParallelJDTCompiler

1min20s

4 threads JDTCompiler

1min40s

4 threads ParallelJDTCompiler

1min02s

8 threads JDTCompiler

1min40s

8 threads ParallelJDTCompiler

0min54s

16 threads JDTCompiler

1min39s

16 threads ParallelJDTCompiler

0min49s

Notes

There is a incompatibility with maven 3.3.X and version 2.4.3 of jspc-maven-plugin. If you use maven 3.3.X you must take version 2.4.4 of the plugin.

comments powered by Disqus