Got a little performance issue, fired up theĀ profilerĀ and tracked 75% of CPU cycle on this block of code.
public int indexOf(ReportColumn col, UseType use) { ReportColumn copyColumn = col.copy(); copyColumn.setUse(use); return columnList.indexOf(copyColumn); }
The majority of the hold up is in the copy() function which produce a deep clone of the ReportColumn object via reflection. Not only is this very slow, but it also increase the memory needed because of the deep clone process.
My immediate reaction was to use remember the “use” value of the column, make a change to the col object, find the index and change it back. But then I started to worry about the original intent of the function, since I didn’t write it, I just assume whom ever did write it this way must use deep cloning for a reason. Maybe they expect col object to be read by another thread while the search is happening. Hence I just abandoned plan A.
Plan B involved proxying, and went a bit like this.
public int slowIndexOf(ReportColumn col, final UseType use) { ProxyFactory pf = new ProxyFactory(); pf.setTarget(col); pf.addAdvice(new MethodInterceptor() { @Override public Object invoke(MethodInvocation mi) throws Throwable { if (mi.getMethod().getName().startsWith("getUse")) { return use; } else { return mi.proceed(); } } }); ReportColumn copyColumn = (ReportColumn) pf.getProxy(); return columnList.indexOf(copyColumn); }
By using the spring’s inbuilt proxying libraries, I constructed a method interceptor to intercept call to getUse and replace the value with the use we want to search for.
However, as the name indicates, it was very slow, slower than I thought it would be, in fact pf.getProxy() was 4 times slower than the deep clone method.
Back to the drawing board, I ended up tracing the code to see if I can find any potential code that might be reading the col object while it is been used, hoping that I can try a locking to prevent that from happening. As it turns out, there is no concurrent access. So I ended up with this.
public int indexOf(ReportColumn col, final UseType use) { UseType oldUse = col.getUse(); col.setUse(use); int index = columnList.indexOf(col); col.setUse(oldUse); return index; }
Lesson here, first instinct/simplest solution is often the best, also question your initial assumptions.