https://bz.apache.org/bugzilla/show_bug.cgi?id=68119

            Bug ID: 68119
           Summary: Significant overhead in
                    javax.el.CompositeELResolver.convertToType
           Product: Tomcat 9
           Version: 9.0.x
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: EL
          Assignee: dev@tomcat.apache.org
          Reporter: jeng...@amazon.com
  Target Milestone: -----

A high-volume, latency-sensitive application shows that approximately 0.8% of
cpu is spent executing javax.el.CompositeELResolver.convertToType.  Of this, >
95% is spent locally within the method, and the remainder is spent within
javax.el.ELResolver.convertToType.

The source code for CompositeELResolver.convertToType is:


    @Override
    public Object convertToType(ELContext context, Object obj, Class<?> type) {
        context.setPropertyResolved(false);
        int sz = this.size;
        for (int i = 0; i < sz; i++) {
            Object result = this.resolvers[i].convertToType(context, obj,
type);
            if (context.isPropertyResolved()) {
                return result;
            }
        }
        return null;
    }

The source code for ELResolver.convertToType is:

    public final Object convertToType(ELContext context, Object obj, Class<?>
type) {
        context.setPropertyResolved(false);
        return null;
    }

The high cost of CompositeELResolver.convertToType is because the loop uses
vtable lookup for method invocations (INVOKEVIRTUAL); the calls cannot be
inlined because ELResolver.convertToType can be overridden by any of
approximately 15 implementations.

However, within the Tomcat codebase, the only implementation that actually DOES
anything is this CompositeELResolver.  In effect, we're paying method
invocation overhead that is guaranteed to do nothing except, perhaps,
recursively do more nothing.

Recognizing the potential need for custom implementations of
ELResolvert.convertToType, we may want to maintain the current method but strip
out the "known" implementations, such as those defined in
ELContextImpl.getDefaultResolver().  This would allow the JIT compiler to
optimize away loops over arrays of length zero, one, or two while still
accommodating applications that directly call CompositeResolver.add().  

Note that the array CompositeELResolver.resolvers is also used in other methods
(for better reasons) and this may require a distinct array for convertToType.


ELResolver.convertToType is relatively cheap, but is called so often that the
aggregate is detectable by our tools.  While the task of setting false to false
is truly redundant, the changes suggested above are sufficient to eliminate the
penalty with no further effort.

Performance tests with a zero-length array show a 95% reduction in runtime;
removing the code completely shows a 100% reduction in runtime.

-- 
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to