Embed StringBundler into App-server

If you have read my previous blog, you must already know StringBundler.

StringBundler can improve your String processing performance a lot, but it is still not enough.

Now answer this question: In a web application, which part creates most String garbages?

Normally it is not your Java code, but the template engine for generating the page content. Most likely is Jsps. Jsps will be complied into Servlets before providing services. Jsp's performance depends on Jsp Compiler's optimization skills. Well not a single Jsp Compiler knows StringBundler(It is liferay's internal code), so they must either use StringBuffer, StringBuilder or some similar home-made way. So can we improve jsp's String processing performance? It seems almost impossible without modify Jsp Compiler's code. Thanks to the well designed extendable Jsp framework. It is actually doable!

Let's skip all the explanation and impl detail, directly see the optimization result.

By adding the follow line to your code(you can add it to any where, but for better performance, i suggest to add it into startup progress):

JspFactorySwapper.swap();

You can improve String processing performance significantly. How much that would be actually? In our MessageBoard benchmark test case, this line reduces 66% old generation collection.

Now let me explain what kind of things this simple line do to provide you the improvement.

If you are familiar with Jsp standard, you must know PageContext. It is the center of the bottleneck. It has two performance problems.

  1. It has a JspWriter, and Jsp standard does not provide a standard way to configure that writer's buffer, most Jsp Compilers make it a fix size, a normal size is 8K.
  2. It is responsible for creating BodyContent, and most app-server's BodyContentImpl uses a StringBuilder like way to collect page data.


For 1, if you have read my IO performance blog, you should know this buffer is actually hurting your performance by introduce unnecessary data copying. So we should disable the buffer.

For 2, if we can use StringBundler to collect the data, we can avoid a lot of intermediate data buffers.

Jsp standard provides a way to hack into its internal code, without modify any app-server's code. We can install our own JspFactory instance by:

JspFactory.setDefaultFactory(newJspFactory);

And in our JspFactoryImpl, we wrapper out the actual PageContextImpl to disable the writer buffer and wrapper the BodyContentImpl with a StringBundler enabled wrapper.

The whole process is following Jsp standard, no reflection and code modification is required, so theoretically it should work on all app-servers, as long as their also follow the Jsp standard. And in practice, it works on all app-servers except WebLogic. WebLogic's Jsp Compiler is not following the rules programming to interface, the compiled servlet code assumes all code are their internal impl code, it does downcast everywhere. So i am sorry say we have to do this optimization without WebLogic.

For more impl detail, please see LPS-9099

Blogs
[...] String Concatenation Performance vs. String Builder/Buffer and how Liferay 6 achieved a speedup by not using S.B. [that much] – StringBuilder/Buffer has lot of overhead and thus String.concat or... [...] Read More