- Performing in the Cloud
- Measuring the Cost of Class Loading
- Avoiding Cold Startups
- Improving Performance in General
- Summary
Measuring the Cost of Class Loading
Every library or framework you introduce brings lots of additional classes to load at startup. For this reason, this book introduces only three third-party JARs to help with the code examples: Commons FileUpload, StringTemplate, and ANTLR. Commons FileUpload is used to process form submits with files as content. StringTemplate is used as a template language to generate output for the visitors, and it can also be used to generate text for an e-mail. ANTLR stands for Another Tool for Language Recognition and is a dependency of StringTemplate.
To show you the cost of class loading, this chapter investigates the startup time of the App Engine instance with StringTemplate and without StringTemplate. In addition, there is a startup time comparison between a web.xml file of roughly 400 lines and a web.xml of 21 lines.
Timing a Servlet That Contains a Library
Listing 2.1 shows a very simple servlet that processes a template using the StringTemplate framework and shows “Hello, World” in the browser window.
Listing 2.1 Writing Hello World with StringTemplate
01 package com.appspot.template; 02 03 import java.io.IOException; 04 05 import javax.servlet.ServletException; 06 import javax.servlet.http.HttpServlet; 07 import javax.servlet.http.HttpServletRequest; 08 import javax.servlet.http.HttpServletResponse; 09 10 import org.antlr.stringtemplate.StringTemplate; 11 import org.antlr.stringtemplate.StringTemplateGroup; 12 13 public class StringTemplateServlet extends HttpServlet { 14 15 protected void doGet(HttpServletRequest request, 16 HttpServletResponse response) 17 throws ServletException, IOException { 18 long startTime = System.currentTimeMillis(); 19 20 StringTemplateGroup group = new StringTemplateGroup("xhtml", 21 "WEB-INF/templates/xhtml"); 22 StringTemplate hello = group.getInstanceOf("hello-world"); 23 hello.setAttribute("name", "World"); 24 response.getWriter().write(hello.toString()); 25 26 long diff = System.currentTimeMillis() - startTime; 27 response.getWriter().write("time: " + diff); 28 29 } 30 }
Lines 18 and 26 process the timer, while the code loading the StringTemplate and ANTLR JARs are on lines 20 through 24.
Writing the resulting time at the bottom of the HTML (line 27) is not really elegant, but it works sufficiently for the simple timer required in this example.
Line 22 refers to an external file with an HTML template. This template is shown in Listing 2.2.
Listing 2.2 Setting Up the HTML Template for StringTemplate
01 <html> 02 <head> 03 <title>Test</title> 04 </head> 05 <body> 06 Hello, $name$ from a file! 07 </body> 08 </html>
Line 6 processes the attribute provided in line 23 of Listing 2.1. The rest of the HTML template should not require any explanation. The resulting screen just after a new instance is launched is displayed in Figure 2.1.
Figure 2.1 Displaying the resulting time in the browser screen with StringTemplate.
Reloading the same servlet when the instance is already started is a lot faster. Processing the StringTemplate takes 10 to 15 milliseconds on subsequent requests.
Timing a Servlet That Does Not Contain a Library
Writing Hello World to a browser screen is simple enough to do without a library like StringTemplate. If you modify the code to write Hello World directly to the browser, you get a servlet as shown in Listing 2.3.
Listing 2.3 Writing Hello World without StringTemplate
01 package com.appspot.template; 02 03 import java.io.IOException; 04 05 import javax.servlet.ServletException; 06 import javax.servlet.http.HttpServlet; 07 import javax.servlet.http.HttpServletRequest; 08 import javax.servlet.http.HttpServletResponse; 09 10 public class StringTemplateServlet extends HttpServlet { 11 12 protected void doGet(HttpServletRequest request, 13 HttpServletResponse response) 14 throws ServletException, IOException { 15 16 long startTime = System.currentTimeMillis(); 17 18 response.getWriter().write("Hello World without ST! "); 19 20 long diff = System.currentTimeMillis() - startTime; 21 response.getWriter().write("time: " + diff); 22 23 } 24 }
The only difference is in line 18. To avoid wasting too much code, the HTML is left out. Seven short lines of HTML do not have a significant influence on the loading time: they account for less than a millisecond.
Figure 2.2 shows the browser window loading the servlet from Listing 2.3 while starting a new instance. The decrease in loading time is substantial!
Figure 2.2 Displaying the resulting time in the browser screen without StringTemplate.
If loading the StringTemplate library increases the loading time of a new App Engine instance by 300 milliseconds, then why not switch to FreeMarker, Velocity, or Java Server Pages (JSP), you might ask. Or perhaps you know another template engine not mentioned here. You are encouraged to investigate and find out for yourself which library has the most efficient loading times on cold startup.
For any other library or framework you’d like to introduce, you should first investigate what the effect is on the total load time. Adding an additional JAR is always a big step.
Reducing the Size of web.xml
Explicit changes like adding JARs are relatively simple to manage. More tricky is making changes more gradually over time. For example, this book is full of servlets. As servlets were added, the web.xml file grew. At the end of the writing, the web.xml file contained more than 400 lines of configuration setting up all the examples demonstrated in the book.
The number of servlets declared in web.xml has a significant influence on the class loading time. To test the difference, the web.xml was reduced to minimal size, as shown in Listing 2.4. Just a single servlet is included—the servlet from Listings 2.1 and 2.3.
Listing 2.4 Reducing web.xml to an Absolute Minimum
01 <?xml version="1.0" encoding="utf-8"?> 02 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 03 xmlns="http://java.sun.com/xml/ns/javaee" 04 xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 05 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 06 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 07 version="2.5"> 08 09 <!-- Template --> 10 <servlet> 11 <servlet-name>StringTemplateServlet</servlet-name> 12 <servlet-class> 13 com.appspot.template.StringTemplateServlet 14 </servlet-class> 15 </servlet> 16 <servlet-mapping> 17 <servlet-name>StringTemplateServlet</servlet-name> 18 <url-pattern>/st</url-pattern> 19 </servlet-mapping> 20 21 </web-app>
Take a look at the log files before and after the web.xml size reduction. Figure 2.3 shows the difference in CPU usage for both scenarios.
Figure 2.3 Displaying the logged CPU times before and after a web.xml reduction.
As you can see, the difference in load time on cold startup is significant. This is an indication that you should be careful with the number of servlets you declare in a web application. On the other hand, one very large servlet is unlikely to perform much better than several smaller ones, so you must consider the trade-off. How do you divide your code over a number of servlets with the least class loading overhead? Again, there is no silver bullet for doing so. The important thing is that you think about this trade-off in your specific situation.