... your performance goes down the drain.

This problem caught me a couple of years ago and I ran into it recently again...

Years ago we designed an interface to a remote system to query some specific customer details. In case customer details could not be found by the remote system, an exception would be returned. Using an exception to deal with this case seemed valid, because (according to our client) the exception would only occur in max. 2% of the requests. During performance testing with production alike data we were not able to reach the required performance goals, performance was really bad. After running some profilers we discovered that most time was wasted on exception handling. It appeared that >50% of the requests was resulting is an exception because these specific customer details were not available. The incorrect assumption about the availability of those customer details  was seriously impacting the performance. After discovering this we decided to have the interface return 'null' in case no customer details could be found (thus avoiding exceptions) and performance was way over the goals we had to meet.

Why am I writing this? Well, I ran into the same problem with the OSS/J TCK Foundation. Performance of the OSS/J TCK Foundation was not satisfying and I was assuming this was due to the heavy use of reflection. However, after running some tests it appeared that although reflection related operations were consuming a significant amount of time, exception handling was consuming even more time. So again, if we could come up with a solution that did not result in that many exceptions, performance would be improved. Simply returning null, was not an option this time.

In the TCK Foundation we're using cglib to at runtime deal with subtypes of the OSS/J Common API class that are defined by either the specific OSS/J API or by a Reference Implementation of the OSS/J API. By doing this the TCK Foundation can be used to build TCKs for all OSS/J API because it is capable to handling OSS/J API specific extensions without need to write specific code.
In a
MethodInterceptor.intercept(java.lang.Object obj, java.lang.reflect.Method method, java.lang.Object[] args, MethodProxy proxy))
implementation we were always first invoking the requested operation on the
proxy object using proxy.invokeSuper() and only when it returned an NoSuchMethodException (for example when the method was not implemented by the object) we would apply some other logic to handle the requested method appropriately.

The optimization  was to cache the methods that resulted in a NoSuchMethodException in static variable and before invoking the method on the proxy we first check if the method is already registered as being not implemented. If we already know it is not implemented, there is no need to invoke the method again on the proxy. Now we only get the NoSuchMethodException once for each not implemented method and after that we know we need to apply the other logic.

This (again simple) fix  doubled the performance!

Next thing is to see if we can eliminate some of the reflection logic, profiling showed that this is now the biggest performance bottleneck (although performance is acceptable now).


Post a Comment