The ever-vigilant David Leedy pointed me to a LinkedIn conversation about XPages performance tips this evening that lead me down a particularly interesting rabbit hole.
Those of you who’ve been following the XPages story for a while know about my disdain for SSJS. What started as a criticism of performance later evolved to a criticism of programming practices. However it’s worth noting that in the meantime, one of the principle performance criticisms has been addressed. You see, SSJS is no longer strictly an interpretted language. It has it’s own version of expression compiling and tree-caching on an application level.
I don’t know what version this was introduced in. It may have been there all along. It’s certainly not something that’s easily discovered. When I found out how the cache is defined, I goggled the xsp.properties setting (ibm.jscript.cachesize) and found 3 references to it: 1) The XPages Portable Command Guide; 2) An XPages performance presentation from IBM Connect 2013; and 3) a Tweet from Bruce Elgort. And Bruce rightly asks “how come nobody is talking about this?”
I have your answer Bruce: nobody really knew about it. Oh, we might have gone to the session, and we might own a copy of the XPCG, but when you add an enhancement like parsed expression caching to what everyone knows is an interpretted language, you don’t put it in Appendix Q of Redbook 13948. You write it in the sky in 40-foot burning letters and fly supersonic jetfighters through it so no one can look away.
So anyway, yeah. The point is that the complaint about SSJS, that each time a ValueBinding is evaluated the expression is re-parsed, is false. I don’t know when it went from being true to false. Maybe it was false all along. But the thing that makes it false has been a closely guarded secret until Domino 9.
It also answers a question I’ve had for a long time: “why does the generated Java class for XPages include the XPath expression for a value binding along with the actual SSJS source code?” Now I suspect that’s the key used for the cache.
So, in theory — and I’d love it if someone with authoritative knowledge chimed in here — each time an SSJS expression is encountered, once it’s parsed, the resulting AST tree is retained in a cache so that the next time the same expression is processed, there is no need to parse it again. The AST itself can simply be executed against updated values. This is a wonderful enhancement and it leads to some interesting conclusions…
- If you can write less than 400 total SSJS expressions in your application, it’s going to run a lot faster.
- If you write more than 400 total SSJS expressions in your application, it’s still going to optimize the ones used the most
- IBM continues to suck at documenting important features. And they even failed at educating IBM champions who globally promote XPages, since out of all the ones I know, only Bruce Elgort has said anything about this publicly.
Now… all that being said, I’d like to address the title of this post. Because you see, none of what I’ve mentioned so far has anything to do with the performance of XPages applications. Oh sure, if you’ve optimized the snot out of your application and written a brilliant execution of your spec, then yes, I imagine the difference between cached SSJS trees and EL reflection calls against explicit getters will improve your CPU time. But I seriously doubt you’ve dealt with all the other areas in which you can make performance improvements.
That’s not a reflection on you. I haven’t dealt with them either, because it’s really hard to focus on things that matter instead of the things that are easy.
If you want to write fast XPages applications, here’s how you do it…
- Minimize I/O between the client and the server. So avoid POSTs when GETs will do; encourage caching of your resources; use Gzip and resource optimizations like CSS sprites and the automatic aggregation in 9.0; be rigorous about the scope of your partial refresh & execs.
Reducing the amount of network traffic triggered by your application is job #1. The POST vs. GET part is one of my favorite changes.
- Minimize I/O between the XPage and the underlying NSF. Java API access to NSF data is slow. Some parts of XPages do it faster than the regular API, but it’s still slow compared to expectations on other platforms. And just like Notes apps, when you can take advantage of the faster parts of NSF, you can make big gains. ViewNavigators tend to be faster than ViewEntryCollections tend to be faster than Documents. If you can do without Views altogether, and use keyed UNIDs and MIMEBean objects, you can save loads of time.
But even before that, simply putting NSF-based data into scoped variable helps a lot. If you want to display a list of sales territories in a combobox, you probably only need to do your @DbLookup code once and then stick the result in an Application scope variable. If you want to grab the current user’s email address from the Directory, put the result in a Session scope variable so you don’t have to go back to the source data each time.
- Write less code. As it turns out, this is just good general software practice, but it applies to performance as well. The more code you create, the more work the platform has to do to track it and keep it optimized. Much of this has to be done looking down seldom-used paths. If you can accomplish your goals by writing less overall code, you stand a much greater chance of being able to tune that code both manually and through compilers.
In addition, think about my earlier advice to run XPages apps from a single NSF for the whole server. If you have common images and script files from the template, then you’ll be generating a single, cacheable URL for all your apps. If you have common expressions used all over your apps, you’ll get the advantage of the AST caching. If you have common beans for your apps, you’ll constructing and initializing them only once.
- Learn. Pay attention to what’s happening in the world with regards not just to XPages performance, but with Java as a whole. With NSF. With wireless and cellular networks. With solid state drives. With OpenNTF. With Eclipse and Apache and Google Code and GIT and Linux. Run a local dev server so you can do your own benchmarks. Educate yourself on the state of the art in high-speed persistence patterns. Learn about graph databases. Learn about JSON. Learn about REST services.
Because ultimately, the best way to make sure your XPages applications go faster is the same way to make sure your racecar goes faster: improve your skills.