Skip to content

Down the Rabbit Hole

brettwooldridge edited this page Oct 21, 2013 · 51 revisions

We're in your bytecodez

In order to make HikariCP as fast as it is, we went down to bytecode-level engineering. We pulled out every trick we know to help the JIT help you. We tried to limit key routines to less then the JIT inline-threshold, we chased down and eliminated as many invokeinterface or invokespecial bytecode operations as possible, flattened inheritance hierarchies, shadowed member variables, eliminated casts. Trusting no one, we dissected the JVM classes we used and finding them wanting replaced where necessary. We studied operating system thread schedulers and JIT compiler output. We think it shows in our results. But like rust, we never sleep; there are still a few unexplored paths we intend to go down in the future.

There is not only beauty in simplicity, but if done right, speed.

Javassist-generated Delegates

Pretty much every connection pool, dare we say every pool available, has to "wrap" your real Connection, Statement, PreparedStatement, etc. instances and intercept methods like close() so that the Connection isn't actually closed but instead is returned to the pool. Statement and it's subclasses must be wrapped, and SQLException caught and inspected to see if the exception reflects a disconnection that warrants ejecting the Connection from the pool.

What this means is "delegation". The Connection wrapper cares about intercepting close() or execute(sql), but for all of the other methods of Connection it simply delegates. Something like:

public Clob createClob() {
   return delegate.createClob();
}

The first iteration of HikariCP also did this, and it still provides a "fallback" mode. An interface like PreparedStatement contains some 50+ methods, only 4 of which we are interested in intercepting. Rather than creating a wrapper class that has 50+ "delegate" methods like the above, we use Javassist to generate all of the delegate methods. While this provides no inherent performance increase, it means that our "proxy" (wrapper) class only need contain the overridden methods. The Statement proxy class in HikariCP is only ~130 lines of code, including comments. This is in keeping with out minimalist ethos.

Our delegates perform quite admirably:

Pool Med (ms) Avg (ms) Max (ms)
BoneCP 5049 3249 6929
HikariCP 13 11 58

And yet. Looking at bytecode for all of the delegate methods, with their getfield, checkcast, and invokeinterface op codes really touched our nerve. Is it possible to go faster?

Can we actually eliminate delegation itself?

Dark Side of the Moon (Instrumental)

"I've always been mad, I know I've been mad, like the 
most of us...very hard to explain why you're mad,
even if you're not mad..." 

But how? How could we eliminate delegation and still intercept the methods we need? Even still, we need to wrap every "delegate" (non-intercepted) method with a try..catch to interrogate SQLExceptions, which is a kind of interception now isn't it?

Enter java.lang.instrument

Clone this wiki locally