How to avoid Play Framework killing performance
Note: This article is about Play Framework 1.2.x and might not apply to Play 2.x projects.
The other day I had to convert some code from C to Java. This code runs some pretty simple calculations, using a few classes as data-holders, and running a few thousand iterations. I had to get this working as part of the backend for a Play Framework web application.
In C the code would run blindingly fast, and return the results within 10-20 milliseconds. All good!
After a couple of hours of converting the code and fixing the pointer/reference/initialization oddities between Java and C, I got the code running. Well, more like walking, or lazily sauntering along. The Java code took 4 seconds to run. I know Java is perceived to be slow, but my experience with it was telling me that there is no way it should be so slow.
I had a look at the Java code. Lots of Math.Pow(), Math.sqrt() calls, a lot of calculations with doubles and a few BigIntegers. Nothing very scary, and nothing that stood out as being super-slow-inducing code.
So, resisting the initial urge to optimize the code and start fiddling with data types, I fired up VisualVM to have a look at the code when it was running. I run the calculation and *bam*. Many thousands of calls to PropertiesEnhancer.FieldAccessor.invokeReadProperty() and .FieldAccessor.invokeWriteProperty() methods, and they were taking 90% of the CPU-time. My initial thought was: “WTF!”
After looking some more I decided to try to run the code as a standalone Java program, just invoking the calculations with the same parameters as it would be using through the Play framework. The calculation took 30ms. Now I was thinking: “W00000T.THE.FsCK!!”. I was super happy that it wasn’t Java, as I initially expected, but needed to find out what the hell Play Framework was doing to my code.
My research (Google search) indicated the problem: Play Framework intercepts every access to a public field in any class that is run under Play Framework. My translated C-code was full of public variables as a side-effect from converting structs to data-classes. I used my IDE to encapsulate all the public fields in getters and setters, and ran the code again. Runtime now, with getters and setters in Play Framework: 30 ms! Mission complete! The calculation was accessing public fields thousands of times per calculation, and Play was intercepting every single access so it could redirect to Play’s own auto-generated getter-setter code! That is what killed performance, and luckily the solution was pretty simple.
There is more information about the internals of Play’s PropertiesEnhancer class and other Play Framework internals in this great set of slides.
The reason I am writing about this is to help myself remember this crazy Play Framework issue. I like that fact that it auto generates getters and setters, but it can make the code run extremely slow under certain conditions.
Even though it is good practice in general to use getters and setters, I would love for Play Framework to add an option to turn off auto-generation of getters and setters for certain classes. Another fine addition would be to detect and give a warning if the FieldAccessor methods are being spammed.
The conclusion: If your code is running unnaturally slow with Play Framework, check if it could be massive use of public variables.