Extending ColdFusion Components And Its Impact On Page Performance
Posted November 8, 2006 at 2:31 PM
It was lunch time and I was curious as to how extending ColdFusion Components affects page processing time and performance. For those of you who don't know, you can "inherit" the functionality of one component by extending it and overriding functionality. Component instantiation has some overhead in ColdFusion so that leads me to think that using extend has even more overhead... but does it.
To test, I created a Girl.cfc and FullGirl.cfc. The Girl.cfc extends Person.cfc which extends AbstractBaseComponent.cfc. The FullGirl.cfc, on the other hand is "God Object" that can do everything that Girl.cfc does, except that it doesn't need to extend anything.
I then ran tests instantiation X number of components of each type:
Launch code in new window » Download code as text file »
- <!--- Set the test size. --->
- <cfset intSize = 1000 />
-
- <!--- Create an array for the girls. --->
- <cfset arrGirls = ArrayNew( 1 ) />
-
- <!--- Resize the array. --->
- <cfset ArrayResize( arrGirls, intSize ) />
-
-
- <!---
- Test the performance of components that
- extend other components.
- --->
- <cftimer label="Extend Methodology" type="outline">
-
- <!--- Loop over the size and add a girl. --->
- <cfloop index="intIndex" from="1" to="#intSize#" step="1">
-
- <cfset arrGirls[ intIndex ] = CreateObject(
- "component",
- "Girl"
- ).Init(
- FirstName = "Yuu",
- LastName = "Sekine"
- ) />
-
- </cfloop>
-
- </cftimer>
-
-
- <!---
- Test the performance of the a GOD component that
- doesn't need to extend anything.
- --->
- <cftimer label="God Object Methodology" type="outline">
-
- <!--- Loop over the size and add a girl. --->
- <cfloop index="intIndex" from="1" to="#intSize#" step="1">
-
- <cfset arrGirls[ intIndex ] = CreateObject(
- "component",
- "FullGirl"
- ).Init(
- FirstName = "Yuu",
- LastName = "Sekine"
- ) />
-
- </cfloop>
-
- </cftimer>
After a few tests of creating 1,000 instances, these are the results that I got:
Using Extended Components
1: 15,937 ms
2: 16,312 ms
3: 16,312 ms
4: 16,312 ms
5: 16,281 ms
6: 15,765 ms
7: 16,171 ms
8: 15,765 ms
Using GOD Components
1: 16,328 ms
2: 15,718 ms
3: 15,718 ms
4: 15,750 ms
5: 15,827 ms
6: 15,765 ms
7: 16,281 ms
8: 16,296 ms
As you can see, over a 1,000 object instantiations, the GOD object creation was only marginally faster. In fact, the GOD object creation was occassionally slower. I would say that this is a totally negligible difference. This is very good to know. Extending components makes code easier to extend and maintain. It's nice to know this can be done without really sacrificing any performance.
If you are interested in seeing the ColdFusion components that I wrote for this, they are listed below.
AbstractBaseComponent.cfc ColdFusion Component
Launch code in new window » Download code as text file »
- <cfcomponent
- displayname="AbstractBaseComponent"
- output="false"
- hint="Handles base functionality for CFCs.">
-
-
- <!--- Set unique ID for this instance. --->
- <cfset VARIABLES.InstanceID = CreateUUID() />
-
-
- <cffunction name="Init" access="public" returntype="AbstractBaseComponent" output="false"
- hint="Returns an initialized AbstractBaseComponent instance.">
-
- <!--- Return This reference. --->
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction name="EqualTo" access="public" returntype="boolean" output="false"
- hint="Determines if this ">
-
- <!--- Define arguments. --->
- <cfargument name="Comparable" type="any" required="true" />
-
- <!--- Try to compare this object instance to the passed in. --->
- <cftry>
-
- <!--- Compare this ID to that ID. --->
- <cfreturn NOT Compare(
- VARIABLES.InstanceID,
- ARGUMENTS.Comparable.GetInstanceID()
- ) />
-
- <cfcatch>
-
- <!--- An error occurred, return false. --->
- <cfreturn false />
-
- </cfcatch>
- </cftry>
- </cffunction>
-
-
- <cffunction name="GetInstanceID" access="public" returntype="string" output="false"
- hint="Returns the instance's unique ID.">
-
- <cfreturn VARIABLES.InstanceID />
- </cffunction>
-
- </cfcomponent>
Person.cfc ColdFusion Component
Launch code in new window » Download code as text file »
- <cfcomponent
- displayname="Person"
- extends="AbstractBaseComponent"
- output="false"
- hint="Handles base functionality for the 'person' package of objects.">
-
-
- <!--- Set default properties. --->
- <cfset VARIABLES.Instance = StructNew() />
- <cfset VARIABLES.Instance.FirstName = "" />
- <cfset VARIABLES.Instance.LastName = "" />
-
-
- <cffunction name="Init" access="public" returntype="Person" output="false"
- hint="Returns an initialized Person instance.">
-
- <!--- Define arguments. --->
- <cfargument name="FirstName" type="string" required="false" default="" />
- <cfargument name="LastName" type="string" required="false" default="" />
-
- <!--- Store arguments. --->
- <cfset VARIABLES.Instance.FirstName = ARGUMENTS.FirstName />
- <cfset VARIABLES.Instance.LastName = ARGUMENTS.LastName />
-
- <!--- Return This reference. --->
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction name="GetFirstName" access="public" returntype="string" output="false"
- hint="Returns first name.">
-
- <cfreturn VARIABLES.Instance.FirstName />
- </cffunction>
-
-
- <cffunction name="GetLastName" access="public" returntype="string" output="false"
- hint="Returns last name.">
-
- <cfreturn VARIABLES.Instance.LastName />
- </cffunction>
-
-
- <cffunction name="SetFirstName" access="public" returntype="void" output="false"
- hint="Sets first name.">
-
- <!--- Define arguments. --->
- <cfargument name="FirstName" type="string" required="true" />
-
- <!--- Set value. --->
- <cfset VARIABLES.Instance.FirstName = ARGUMENTS.FirstName />
- <cfreturn />
- </cffunction>
-
-
- <cffunction name="SetLastName" access="public" returntype="void" output="false"
- hint="Sets last name.">
-
- <!--- Define arguments. --->
- <cfargument name="LastName" type="string" required="true" />
-
- <!--- Set value. --->
- <cfset VARIABLES.Instance.LastName = ARGUMENTS.LastName />
- <cfreturn />
- </cffunction>
-
- </cfcomponent>
Girl.cfc ColdFusion Component
Launch code in new window » Download code as text file »
- <cfcomponent
- displayname="Girl"
- extends="Person"
- output="false"
- hint="Handles a Girl object.">
-
-
- <!--- Set default properties. --->
- <cfset VARIABLES.Instance.Gender = "Female" />
-
-
- <cffunction name="GetGender" access="public" returntype="string" output="false"
- hint="Returns gender.">
-
- <cfreturn VARIABLES.Instance.Gender />
- </cffunction>
-
-
- <cffunction name="SetGender" access="public" returntype="void" output="false"
- hint="Sets gender.">
-
- <!--- Define arguments. --->
- <cfargument name="Gender" type="string" required="true" />
-
- <!--- Set value. --->
- <cfset VARIABLES.Instance.Gender = ARGUMENTS.Gender />
- <cfreturn />
- </cffunction>
-
- </cfcomponent>
FullGirl.cfc ColdFusion Component
Launch code in new window » Download code as text file »
- <cfcomponent
- displayname="FullGirl"
- output="false"
- hint="Handles all the girl functionality.">
-
-
- <!--- Set unique ID for this instance. --->
- <cfset VARIABLES.InstanceID = CreateUUID() />
-
- <!--- Set default properties. --->
- <cfset VARIABLES.Instance = StructNew() />
- <cfset VARIABLES.Instance.FirstName = "" />
- <cfset VARIABLES.Instance.LastName = "" />
- <cfset VARIABLES.Instance.Gender = "Female" />
-
-
- <cffunction name="Init" access="public" returntype="FullGirl" output="false"
- hint="Returns an initialized Person instance.">
-
- <!--- Define arguments. --->
- <cfargument name="FirstName" type="string" required="false" default="" />
- <cfargument name="LastName" type="string" required="false" default="" />
-
- <!--- Store arguments. --->
- <cfset VARIABLES.Instance.FirstName = ARGUMENTS.FirstName />
- <cfset VARIABLES.Instance.LastName = ARGUMENTS.LastName />
-
- <!--- Return This reference. --->
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction name="EqualTo" access="public" returntype="boolean" output="false"
- hint="Determines if this ">
-
- <!--- Define arguments. --->
- <cfargument name="Comparable" type="any" required="true" />
-
- <!--- Try to compare this object instance to the passed in. --->
- <cftry>
-
- <!--- Compare this ID to that ID. --->
- <cfreturn NOT Compare(
- VARIABLES.InstanceID,
- ARGUMENTS.Comparable.GetInstanceID()
- ) />
-
- <cfcatch>
-
- <!--- An error occurred, return false. --->
- <cfreturn false />
-
- </cfcatch>
- </cftry>
- </cffunction>
-
-
- <cffunction name="GetFirstName" access="public" returntype="string" output="false"
- hint="Returns first name.">
-
- <cfreturn VARIABLES.Instance.FirstName />
- </cffunction>
-
-
- <cffunction name="GetGender" access="public" returntype="string" output="false"
- hint="Returns gender.">
-
- <cfreturn VARIABLES.Instance.Gender />
- </cffunction>
-
-
- <cffunction name="GetInstanceID" access="public" returntype="string" output="false"
- hint="Returns the instance's unique ID.">
-
- <cfreturn VARIABLES.InstanceID />
- </cffunction>
-
-
- <cffunction name="GetLastName" access="public" returntype="string" output="false"
- hint="Returns last name.">
-
- <cfreturn VARIABLES.Instance.LastName />
- </cffunction>
-
-
- <cffunction name="SetFirstName" access="public" returntype="void" output="false"
- hint="Sets first name.">
-
- <!--- Define arguments. --->
- <cfargument name="FirstName" type="string" required="true" />
-
- <!--- Set value. --->
- <cfset VARIABLES.Instance.FirstName = ARGUMENTS.FirstName />
- <cfreturn />
- </cffunction>
-
-
- <cffunction name="SetGender" access="public" returntype="void" output="false"
- hint="Sets gender.">
-
- <!--- Define arguments. --->
- <cfargument name="Gender" type="string" required="true" />
-
- <!--- Set value. --->
- <cfset VARIABLES.Instance.Gender = ARGUMENTS.Gender />
- <cfreturn />
- </cffunction>
-
-
- <cffunction name="SetLastName" access="public" returntype="void" output="false"
- hint="Sets last name.">
-
- <!--- Define arguments. --->
- <cfargument name="LastName" type="string" required="true" />
-
- <!--- Set value. --->
- <cfset VARIABLES.Instance.LastName = ARGUMENTS.LastName />
- <cfreturn />
- </cffunction>
-
- </cfcomponent>
Download Code Snippet ZIP File
Post Comment | Ask Ben | Other Searches | Print Page
Newer Post
Skin Spider : Auto-Tagging Downloaded Videos
Older Post
My Coding Methodology - Understanding The Madness And The Man Behind It
Reader Comments
I appreciate you showing this, I always had a nagging voice in my head wondering if my making well structured code was secretly crippling performance. No worries now!
@Ben,
I can't stress this enough:
Iteration performance tests are worthless.
If you want a true test to see which is more efficient, then you MUST do a proper load test in a proper environment.
How the hell do you know that a some other process running in the background on your machine interfered with these results? You don't.
I'm sorry it sounds like I'm yelling at you, but I can't stand the fact that every single person in the CF community thinks that writing a loop constitutes a true test of performance. IT DOESN'T.
Tony,
given Ben's CFC's can you give specifics on how this should be tested instead?
Hi Tony,
I agree that load testing will pick up on things that loop testing won't (especially as it relates to multiple thread and garbage collection and the like).
I don't think it is fair to talk about a process causing the disparity, though. That's why Ben ran the test multiple times.
FWIW, would love to see a post some time on best practices on (affordable) load testing for CF. Any postings you'd recommend or would you get a chance to do a quick posting yourself some time? I'd certainly love to learn how to do performance testing properly!
Tony,
No worries on the tone; I am a fan of knowledge and truth. I would love to know how to test things in a more useful manner... I mean, that's why I do this type of stuff in the first place.
It makes me wonder though, what do you suppose the best use of the ColdFusion CFTimer tag would be? I pretty much only use it for testing in this fashion, but if this is not useful, I don't really see any use for the CFTimer tag. Any suggestions?
Ben -
I've don't get good times from cftimer, so I use the standard way (end time - start time).
Patrick,
But even regardless of the CFTimer tag and it's times... as Tony says, this type of testing isn't useful (CFTimer or end-start).
So my question is, if only true load testing is useful for performance testing, then what did the ColdFusion team have in mind when it created the CFTimer tag?
Any updates on this? I'd love to hear Tony's suggestions.
@David,
No updates recently. Sadly, over two years later and I'm still not making full use of ColdFusion components. I need to up this effort!
@Ben,
It's absolutely true that you cannot get accurate benchmarks simply by running stuff in a loop like this. However, you can certainly get good comparative performance numbers. You can get a good sense of whether one technique is generally faster than another technique, at least for the particular workload under test.
In other words, you cannot use this to determine in advance how many servers you will need. But you can use this to determine which of two techniques will be significantly faster than the other.
An easy example: ColdFusion arrays are actually Java objects under the hood. They have Java methods. To find out the size of a ColdFusion array, you can use either <code>ArrayLen(array)</code> or <code>array.size()</code> and you can use either <code>array[i]</code> or <code>array.get(i - 1)</code> to get the element of the array at the given index (using the 1-based indices familiar to ColdFusion). But the version using <code>ArrayLen</code> and <code>[i]</code> will turn out to be much faster. And the simplest way to find this out is to use a loop to get comparative performance numbers.
By the way, I like to use <code>t = GetTickCount(); /* do something many times in a long loop...; */ t = GetTickCount() - t;</code> to get performance numbers rather than using <code>cftimer</code>, because I can store the numbers in one file (Application.cfc) and display them later in another file (admin/timers.cfm) or log them or whatever.
Cheers,
Justice
@Justice,
I am surprised that using the ColdFusion methods is faster than using the direct Java methods. I would think you'd be side-stepping some additional method calls if you go directly to the Java layer.
@Ben,
Surprise! :P It actually makes a lot of sense when you see what's going on under the hood.
When you use Java methods in ColdFusion, ColdFusion has to go and do a bunch of reflection to find the right method, convert the parameters to the native Java types, call the method, etc. Reflection is slow.
But when you use <code>ArrayLen</code>, ColdFusion can "turn on" some optimizations. It will check that the object is of the right type and then it calls the <code>size</code> method directly, internally within the <code>ArrayLen</code> function. There is no reflection involved, so that makes <code>ArrayLen</code> significantly faster.
Because ColdFusion is a dynamic language (dynamic in the sense that functions are looked up by name-and-signature resolution/reflection, rather than by a single-instruction function pointer dereference), method invocation is slow. Not terribly slow, because ColdFusion, the Java libraries, and the Java runtime all try to cache some things to speed up reflection, but noticeably slow.
Calls to the built-in ColdFusion functions, however, are not dynamically bound. They are statically bound. In other words, it's the same as a single-instruction function pointer lookup from statically-typed languages. That makes the built-in ColdFusion functions much faster, because invoking them does not rely on reflection.
Cheers,
Justice
Assuming it's correct, that just blew my mind.
@Justice, @David,
Right?!?! That is very fascinating. I really appreciate you clearing that up - I definitely thought just the opposite (not based on any evidence, just theory).
Sidenote: Ben, just noticed your main page is /index.cfm and all of your entries and the member profiles rock a .htm extension.
What the what? Is that some kind of trick-people-and-computers-into-thinking-it's-1990-again caching or pyramid scheme technique you're using? What's going on? I'm guessing the htm files don't actually exist and that you're using url simulation like the majority of other sites in the world, but if so, why the .htm suffix? Very curious.
@David,
The HTM files don't actually exist. I used to use 404-handling; now, I use URL rewriting with IIS-MOD-Rewrite. I chose the HTM file extension years ago before I had url rewriting skills and before ColdFusion has onMissingTemplate(). It was the only way I could "Catch" a missing page without ColdFusion invoking something internal.
Also, I was told a long time ago that htm files appear more "static" to Search Engines; but, that could just be old-school thinking.
Ah gotcha, makes perfect sense.




