1
votes

We are pulling in hierarchical data into our Coldfusion application and parse it into numerous CFCs. The CFC in question stores an array of its children, which are also instances of the same type of component. Parsing some of this data may lead to having to create 100 or more (the most ever would be like 300) CFCs by the time it is done...

Sometimes, creating all of these CFCs can take only 50-75ms, sometimes it takes 1000-1250ms. (I made a script that loops and just creates and stores these objects, using GetTickCount() to keep track of the speed.) I know server load probably has a lot to do with this, but was wondering if there was some way to improve the performance of creating these objects?

(The first battle was getting all of the data for these objects in one DB call, which we were able to do, but now that runs consistently, we found that instantiating a bunch of these CFCs may also be a bottleneck.)

3
What JVM are you currently on (provide major and minor version no.), some JVMs (including the one that ships with CF8) are known to have object-creation overhead that is resolved/improved in later JVMs. - Shawn Holmes
This is our Java build: 1.6.0_25-b06. In case it matters we are running Coldfusion 8 on a Windows 2003 Server... (I know, way outdated, which is probably part of our issue, but I cannot control that.) - jzimmerman2011
Any particular reason the design of the CF app fell into the realm of "we'll need to create an object for every single piece of data we farm out of the query hierarchy." -- CFC object creation is, for all intents and purposes, expensive. - Shawn Holmes
probably just to keep an object oriented design. i'm working with a legacy system; i'm not one of the original developers. i am, however, working on trying to get this to work faster. before, each CFC made like 2-3 queries to get it own data, then did the same for each child, etc. so, i was able to get (possibly) 800+ db calls down to just 2-3 total for the whole hierarchy... i know that the cfcs can be loaded in the realm of 100ms, i was wondering if there was maybe some things to check that would make this more consistent rather than 100ms here and 1000ms there. - jzimmerman2011
My initial gut tells me there will be no magical fix to this oversight in design. There's a good chance that some objects take longer than others due to the way their hooked to one another (ie. your implication of hierarchical data, therefore parent-child relationships being calc'd on-the-fly, rather than cached). If the objects are large enough, they may also cause a bit of GC churn. I'd advise some more granular CFTIMER wrapping to find actual code bottlenecks. Some re-factoring may be needed, sadly. - Shawn Holmes

3 Answers

3
votes

The most efficient thing you can do is not recreate the object on every request. How you go about this depends on the particular use case. One thing that might work for you if you are not dealing with a massive number of unique objects is to have a cache struct in application scope keyed by something like "#tablename#--#primarykey#". When you are about to create an object first check whether the primarykey exists in application scope, if it does then you can just update the data in the object and use it, otherwise create a new object. It is the initial creation of the object that is the most taxing part of the process.

<cfif not structkeyexists(application, "objectcache")>
    <cfset application.objectcache = {} />
</cfif>
<cfloop query="children">
    <cfif not structkeyexists(applicatin.objectcache, "childtable--#ID#")>
        <cfset application.objectcache = createobject(...).init(...) />
    </cfif>
    <cfset arrayappend(variables.instance.children, application.objectcache["childtable--#ID#"]) />
</cfloop>

You might also want to throw a cflock around the code to prevent issues. If you do have a large number of objects this can still be helpful as you could cache the 10,000 most recent by using a date flags in the object or in another struct.

2
votes

The bottom line is that object creation in ACF has always been memory intensive. It's gotten better with ACF9, but not by much. I can guarantee that taking the same code and run it on Railo (you might have to modify it slightly), you will see a HUGE improvement in CFC creation and even memory consumption. This is not to tell you to switch engines for your project, it's just to tell you that if you're using ACF as your engine, you need to rethink your apps' architecture from using too many CFCs.

That said, the first thing to do is ask yourself, do you really need to be creating so many CFCs? As much as people want to believe that CFCs are the swiss army knife of CFML, they really are a double edged sword. Though they can encapsulate logic for you, they come with the performance trade off. Very often I've seen data stuffed into CFCs that can be rewritten to use arrays and structs to represent the data structure.

So in your case, rather then hitting your head against the wall with trying to get ACF to do something that it will never do (object creation efficiently), I would look at reworking the code to use arrays and structures and only use CFCs when absolutely necessary.

1
votes

One option would be to refactor your code to do the following: utilize CFCs on create and update functions (where persistence is required) and utilize structures on reads. It is much less expensive to create a structure (a hash map) than a CFC.

Your views would almost always then refer to a structure version of an object and its children, while your model would need to handle both the structure version and the CFC version when making attribute calls. This approach results in inconsistent code in how you directly reference a member of an object, but it will be significantly faster than trying to create hundreds or thousands of CFCs at once.