Testing Performance Overhead Of Creating Java Classes In Lucee CFML
UPDATE (2023-01-13): Please see the comments thread at the bottom. It seems there are definitely cases in which createObject()
calls can become slow, having to do with where the JAR files are being loaded and what settings you are using in your application. The moral of the story is test test test your particular use-case, I suppose (though that feels like a very non-satisfactory answer given how generic the idea of loading Java classes is).
One of the most powerful features of ColdFusion is the fact that it is built on top of Java; and, at any time, we can reach down into the Java layer for additional functionality. The typical way in which we do this is to call createObject("java")
and pass in a Java class name. Historically, I've tended to cache the returned Java class value, operating under the assumption that createObject()
had a lot of overhead. But, I don't think I ever based this assumption on any concrete evidence. As such, I wanted to perform a trite performance exploration regarding the createObject()
function in Lucee CFML.
As with all low-level performance experiments, this should be taken with a grain of salt seeing as I'm running it on my machine with no production traffic. That said, my goal here isn't to see absolute numbers but more to get a sense of relative performance between two different approaches.
To try this out, I'm going to experiment with a Static method on Java's Long
class: .toString()
. In my first loop, I'm going to call createObject()
within every iteration. Then, in my second loop, I'm going to use a cached instance of the LongClass
.
ASIDE: When I say "instance" here, it's a bit fuzzy - it's the instance of the class definition, not an instantiated instance of the class. This has to do with the ColdFusion abstraction of Java.
Note that in the following code, I'm outputting one value within each loop as a means to make sure that the ColdFusion compiler isn't trying to "optimize" the loop out of the code (seeing as it doesn't really do anything).
<cfscript>
trialCount = 5;
iterations = 1000000;
echo( "Trials: #trialCount#, Iterations: #numberFormat( iterations )# <br />" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
loop times = trialCount {
echo( "<br />" );
// -- TEST ONE --: Calling the createObject() function every time I need to access one
// of the underlying Java methods.
timer
label = "Calling createObject() every time"
type = "outline"
{
loop times = iterations {
value = createObject( "java", "java.lang.Long" )
.toString( randRange( 1, 100 ), 36 )
;
}
echo( "Last value: #value#" );
}
// -- TEST TWO --: Caching the result of createObject() function, and then reusing
// the cached class when I need to access one of the underlying Java methods.
LongClass = createObject( "java", "java.lang.Long" );
timer
label = "Reusing Cached Class"
type = "outline"
{
loop times = iterations {
value = LongClass.toString( randRange( 1, 100 ), 36 );
}
echo( "Last value: #value#" );
}
}
</cfscript>
Each trial is running 1,000,000 loop iterations for each test. I had to jack this number of pretty high before I started to see any performance difference. And, when we run this ColdFusion code with 1 million iterations, we get the following output:
As you can see, there is some performance overhead to calling the createObject()
function. However, it is extremely small, showing only about a 70ms aggregate overhead over the course of 1 million invocations. Essentially, there is no difference.
I'm glad I did this because caching the createObject()
value adds complexity to my ColdFusion code. And, seeing that there's no meaningful overhead in calling the createObject()
function, I'm going to stop trying to cache these values. Clearly, it was a premature optimization; and, I'm happy to be rid of it.
Want to use code from this post? Check out the license.
Reader Comments
After I posted this, several people over on Twitter (Brad Wood, Michael Born, James Moberg) pointed out that if you are loading Java classes from a 3rd-party JAR file, then calling
createObject()
can have a more significant impact.Part of the reason that I wanted to use a Static Method in my demo was to try and isolate the Class definition cost from the Instantiation costs. But, it seems when you start dealing with dynamically loaded JAR files, things can start to get whacky.
Moral of the story, test test test your assumptions!
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →