Bug 68546 - Performance optimization in PageContextImpl.getELContext()
Summary: Performance optimization in PageContextImpl.getELContext()
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 9
Classification: Unclassified
Component: Jasper (show other bugs)
Version: 9.0.81
Hardware: All All
: P2 normal (vote)
Target Milestone: -----
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-01-26 16:54 UTC by John Engebretson
Modified: 2024-03-06 21:50 UTC (History)
0 users



Attachments
Speed test (3.52 KB, text/plain)
2024-01-26 16:54 UTC, John Engebretson
Details

Note You need to log in before you can comment on or make changes to this bug.
Description John Engebretson 2024-01-26 16:54:10 UTC
Created attachment 39554 [details]
Speed test

A high-volume, latency sensitive application reports that org.apache.jasper.runtime.PageContextImpl.getELContext() uses 0.11% of cpu, of which 0.07% is local to the method.  A portion of that comes from iterating across sparse HashSets (packageImports and classImports), the cost of which grows linearly with array size (null buckets are scanned).

In the most common case, our JSPs rely on the three auto-generated packages and 1-2 classes.  For example, this generated code block from our busiest JSP:

 static {
    _jspx_imports_packages = new java.util.HashSet<>();
    _jspx_imports_packages.add("javax.servlet");
    _jspx_imports_packages.add("javax.servlet.http");
    _jspx_imports_packages.add("javax.servlet.jsp");
    _jspx_imports_classes = new java.util.HashSet<>();
    _jspx_imports_classes.add("com.<proprietary>ViewModel");
    _jspx_imports_classes.add("com.<proprietary>Helper");
  }

Most JVMs initialize HashMap arrays to length 16, so the code above generates one array that is 3/16th full and another that is 2/16th full.  Each page request scans the full 16-slot array.

A few alternatives exist:
1. Right-size the Set allocation.  The entries in the Set are fixed and known at the time of code generation so should be an easy change.
2. Switch to LinkedHashSet<>.  Iteration becomes trivial.
3. Combine the two: right-size a LinkedHashSet<>.

The attached speed test shows a big improvement with #2 (right-sizing) but a greater improvement from either #1 or #3.
Comment 1 John Engebretson 2024-01-26 16:56:57 UTC
Sorry, error with the numbering in the last sentence: the attached speed test shows a big improvement with #1 (right-sizing) but a greater improvement from either #2 or #3.
Comment 2 Remy Maucherat 2024-01-26 17:25:58 UTC
Ok, this sounds reasonable looking at the code.
https://github.com/apache/tomcat/blob/main/java/org/apache/jasper/compiler/Generator.java#L623
Comment 3 Remy Maucherat 2024-01-29 10:43:56 UTC
Thanks !
The fix will be in 11.0.0-M17, 10.1.19, 9.0.86 and 8.5.99.
Comment 4 John Engebretson 2024-03-06 21:50:45 UTC
This changed reached prod and had the (small) expected impact.  Thanks!