Copy link to clipboard
Copied
I have a bunch of web apps that use <cfinvoke> statements to call CFCs. After a recent CF Server Admin class, our instructor mentioned that <cfinvoke> was VERY slow, and that I should be using CreateObject() instead, because it's more efficient.
So my question is, has anyone done any comparisons to see the actual difference? I'm curious to hear other people's experiences with this.
Copy link to clipboard
Copied
My understanding is that this is a bit of an oversimplification, and depends on how people usually use CFINVOKE and createObject. The efficiency comes by instantiating an object and keeping it around in memory, rather than reinstantiating it every time it's needed. By default, when you use CFINVOKE, that's how it works. When you use createObject, you can choose whether you want to store an object reference in a variable a bit more naturally, so people are more likely to do that. But it is possible to create an object reference with your first CFINVOKE, then use subsequent ones to refer to that reference if you really want to.
All that said, I think you're better served by using createObject for this, as it's a more intuitive syntax for manipulating objects - or at least, it's more sensible if you're coming from other programming languages.
Dave Watts, CTO, Fig Leaf Software
Copy link to clipboard
Copied
BreakawayPaul wrote:
I have a bunch of web apps that use <cfinvoke> statements to call CFCs. After a recent CF Server Admin class, our instructor mentioned that <cfinvoke> was VERY slow, and that I should be using CreateObject() instead, because it's more efficient.
There is very little justification for you to make a general comparison between cfinvoke and createobject on the basis of speed or efficiency. They are 2 different things. In fact, I will now present arguments showing that, in some ways, cfinvoke is the more efficient.
To compare like with like, you have to compare cfinvoke with the following 2 actions: 1) instantiation with createobject; 2) invocation of a method on the object created in 1).
Cfinvoke creates an object and invokes a method in one go. There is then an infinitesimal time gap between the two actions. Whereas, createobject may involve an arbitrary amount of latency. If you create an object with createobject, and then invoke a method on the object later in the code, the time gap between the two actions can be arbitrarily large.
Moreover, cfinvoke instantiates the component transiently (that is, the resulting object is not serializable). The object you instantiate with createobject is serializable which, I would imagine, requires more overhead in terms of CPU resources.
Copy link to clipboard
Copied
Moreover, cfinvoke instantiates the component transiently (that is, the resulting object is not serializable). The object you instantiate with createobject is serializable which, I would imagine, requires more overhead in terms of CPU resources.
The reason a transient object - in the example of using one with <cfinvoke> - isn't serialisable is because one never actually gets given an object reference to then use in the objectSave() call. It's got nothing to do with whether the object reference is created using either <cfinvoke> or createObject(), it's that there is no object reference at all. Transience is the lack of a reference being created (and all the knock-on effects that has, one of which the object can't be serialised); the inability to serialise the object is just a symptom of that.
--
Adam
Copy link to clipboard
Copied
Adam Cameron. wrote:
Moreover, cfinvoke instantiates the component transiently (that is, the resulting object is not serializable). The object you instantiate with createobject is serializable which, I would imagine, requires more overhead in terms of CPU resources.The reason a transient object - in the example of using one with <cfinvoke> - isn't serialisable is because one never actually gets given an object reference to then use in the objectSave() call. It's got nothing to do with whether the object reference is created using either <cfinvoke> or createObject(), it's that there is no object reference at all. Transience is the lack of a reference being created (and all the knock-on effects that has, one of which the object can't be serialised); the inability to serialise the object is just a symptom of that.
Adam, you misunderstand my logic. Your counterargument therefore fails to counter my argument.
Reread what I wrote. I used 'serializable' to illustrate my point, my point being "cfinvoke instantiates the component transiently".
Copy link to clipboard
Copied
BKBK wrote:
Adam Cameron. wrote:
Moreover, cfinvoke instantiates the component transiently (that is, the resulting object is not serializable). The object you instantiate with createobject is serializable which, I would imagine, requires more overhead in terms of CPU resources.The reason a transient object - in the example of using one with <cfinvoke> - isn't serialisable is because one never actually gets given an object reference to then use in the objectSave() call. It's got nothing to do with whether the object reference is created using either <cfinvoke> or createObject(), it's that there is no object reference at all. Transience is the lack of a reference being created (and all the knock-on effects that has, one of which the object can't be serialised); the inability to serialise the object is just a symptom of that.
Adam, you misunderstand my logic. Your counterargument therefore fails to counter my argument.
Reread what I wrote. I used 'serializable' to illustrate my point, my point being "cfinvoke instantiates the component transiently".
OK, maybe it's because you're not a native English speaker, but that's not what you actually said.
I think perhaps you meant something like "cfinvoke instantiates the component transiently (for example, the resulting object is not serializable)". Although it's a poor way of articulating your intent, even if that's what you were doing, because you are implying that somehow serialisability is related to object transience, which it isn't other than [well see my previous post].
However you have clarified what you meant, and we're both on agreement with that.
--
Adam
Copy link to clipboard
Copied
Adam Cameron. wrote:
OK, maybe it's because you're not a native English speaker, but that's not what you actually said.
I think perhaps you meant something like "cfinvoke instantiates the component transiently (for example, the resulting object is not serializable)". Although it's a poor way of articulating your intent, even if that's what you were doing, because you are implying that somehow serialisability is related to object transience, which it isn't other than [well see my previous post].
Man, you don't know how to bow out gracefully, do you? Have we ever spoken? Do you know my nationality? My mother-tongue? My skill in English? It's quite rude for you to presume I'm not a native speaker, don't you think?
I'll roll out the logic again, for the last time, I should hope. Fact: "cfinvoke instantiates the component transiently". Consequence: "the resulting object is not serializable".
There was no confusion in language. You simply misunderstood the logic, and went all pedantic as usual. Simple as that.
Copy link to clipboard
Copied
Man, you don't know how to bow out gracefully, do you? Have we ever spoken? Do you know my nationality? My mother-tongue? My skill in English? It's quite rude for you to presume I'm not a native speaker, don't you think?
I seem to recall you have said in the past that you're Dutch and English is not your native language: I'm only going on what you said. Is that wrong?
I happen to think your English is pretty bloody good (and better than an awful lot of the native speakers around here... ;-); it just let you down a bit in this instance. It might have just be unfortunate wording I guess. Or, more realistically, an example of your common approach to contorting logic and retconning what you've said to avoid having to concede you're not right about something.
There's nothing wrong with presuming someone's native language is not English. This does apply to the bulk of the people on the planet, after all.
As for bowing out gracefully... well, Mr Pot: let me introduce you to Mr Kettle.
Anyway, I give up. Whatever.
--
Adam
Copy link to clipboard
Copied
I seem to recall you have said in the past that you're Dutch and English is not your native language: I'm only going on what you said. Is that wrong?
I have lived in The Netherlands. However I am not Dutch, at least not yet, and would never have said I was!
In any case, this isn't just about idly mentioning a fellow forum-visitor's language level. I do believe that it is bad manners, in an argument, to excuse someone else's language shortcoming for them. Especially when the chances are high the misunderstanding could be yours!
You seem to find it hard to admit a mistake. As for your remark about contorting logic and the rest of it, I'll leave it as usual to the reader. The beauty of published text is that it is there for everyone to replay.
Copy link to clipboard
Copied
I seem to recall you have said in the past that you're Dutch and English is not your native language: I'm only going on what you said. Is that wrong?
I have lived in The Netherlands. However I am not Dutch, at least not yet, and would never have said I was!
OK, sorry about that bit.
--
Adam
Copy link to clipboard
Copied
Thanks for the replies!
The apps in question were written many moons ago when we were using a much older version of ColdFusion. Once we upgraded to CF8, I decided to "clean up" some apps, so I tried to move things to CFCs to cut down on code bulk.
This may not be the best way to use CFCs, but I used them to hold some of the queries we use. So basically I have something like this:
<cfif structkeyexists(FORM,"submit)>
<cfinvoke component="records" method="add">
</cfif>
If someone submits the add record form, the insert is done in the CFC. Since more than one page uses a form to add a record, I can use the CFC to only have one copy of the SQL statement. In some examples I have a <cfinvokeargument> to pass more items to the CFC.
In the new version of the above code, I have this instead:
<cfif structkeyexists(FORM,"submit)>
<cfset adduser = CreateObject("component","records").add()>
</cfif>
Which basically does the same thing. So maybe in this case, there isn't going to be much of a performance difference (although the CreateObject version does look a bit cooler)
Copy link to clipboard
Copied
Which basically does the same thing. So maybe in this case, there isn't going to be much of a performance difference (although the CreateObject version does look a bit cooler)
Agree on both counts.
You could perhaps look at which CFCs you have which can safely be instantiated as singletons - ie: they do not maintain any sense of state - and create reusable instances of those in your application scope or similar.
--
Adam
Copy link to clipboard
Copied
BreakawayPaul wrote:
This may not be the best way to use CFCs, but I used them to hold some of the queries we use. So basically I have something like this:
<cfif structkeyexists(FORM,"submit)> <cfinvoke component="records" method="add"> </cfif>
If someone submits the add record form, the insert is done in the CFC. Since more than one page uses a form to add a record, I can use the CFC to only have one copy of the SQL statement. In some examples I have a <cfinvokeargument> to pass more items to the CFC.
In the new version of the above code, I have this instead:
<cfif structkeyexists(FORM,"submit)>
<cfset adduser = CreateObject("component","records").add()>
</cfif>
Which basically does the same thing. So maybe in this case, there isn't going to be much of a performance difference (although the CreateObject version does look a bit cooler)
You've now given us enough to be able to compare. However, to compare like with like, I would add the attribute returnVariable to cfinvoke. We can then put your question more sharply, as follows: which is the faster or more efficient:
<cfinvoke component="records" method="add" returnVariable="adduser">
or
<cfset adduser = CreateObject("component","records").add()>
Why take anyone's word for it? There is at least one test you could do yourself. Here it comes:
<cfset t1=getTickCount()>
<cfset adduser = CreateObject("component","records").add()>
<cfset t2=getTickCount()>
<cfset t3=getTickCount()>
<cfinvoke component="records" method="add" returnVariable="adduser">
<cfset t4=getTickCount()>
<cfoutput>
createobject execution time: #t2-t1#<br>
cfinvoke execution time: #t4-t3#
</cfoutput>
You could adapt the test to a large number of calls to one function, to a large number of calls to different functions, and so on.
There is another significant factor I thought about later. ColdFusion is a Java application. Under the hood, the cfinvoke and createobject code are each converted into Java. The complexity of this Java representation will certainly have an effect on the speed and efficiency.
Copy link to clipboard
Copied
I'm with Dave on this one.
Creating an object and reusing it (which one can just as much do with <cfinvoke> as one can with createObject(), btw, provided one has a constructor method in one's CFC) is going to be more efficient than using a transient object every time one wants to use a method of that CFC. But there's nothing inately slower in using <cfinvoke> than createObject(), as far as I know. However I'd want to load test both solutions before making a concrete claim on that.
The "default" approach with <cfinvoke> is to create transient objects every call, so using that approach will have that overhead each time. However one does not need to create a transient object every time with <cfinvoke>.
Here's code demonstrating like-for-like approaches with both <cfinvoke> and createObject():
<!--- transient objects --->
<cfinvoke component="MyCfc" method="f" result="resultOfF">
<cfinvoke component="MyCfc" method="g" result="resultOfG">
<cfinvoke component="MyCfc" method="h" result="resultOfH">
<cfset resultOfF = createObject("MyCfc").f()>
<cfset resultOfG = createObject("MyCfc").g()>
<cfset resultOfH = createObject("MyCfc").h()>
<!--- persistent object --->
<cfinvoke component="MyCfc" method="init" result="myObject">
<cfinvoke component="#myObject#" method="f" result="resultOfF">
<cfinvoke component="#myObject#" method="g" result="resultOfG">
<cfinvoke component="#myObject#" method="h" result="resultOfH">
<cfset myObject = createObject("myCfc").init()><!--- NB: even calling it without the init() will return an object instance, but I want to keep both bits of code as similar as possible --->
<cfset resultOfF = myObject.f()>
<cfset resultOfG = myObject.g()>
<cfset resultOfH = myObject.h()>
I would expect the different syntactical approaches for transient objects to perform about the same, and again, either syntax for persistent objects would perform about the same.
My reason for using createObject() over <cfinvoke> is that I don't see why I'd want to use a special tag just for creating and using objects. It makes much more sense to just use <cfset>. I really don't know why <cfinvoke> was ever added to the language.
Also note that the reason one might use a transient or a persistent object should not be down to the calling syntax, it should be down to the purpose and implementation of the component. There will be reasons to use transient objects, and reasons to use persistent objects, but the calling syntax should not dictate the usage here.
--
Adam
Copy link to clipboard
Copied
My reason for using createObject() over <cfinvoke> is that I don't see why I'd want to use a special tag just for creating and using objects. It makes much more sense to just use <cfset>. I really don't know why <cfinvoke> was ever added to the language.
My guess as to why it was added is that there are enough cases where it doesn't make sense to keep a reference around, such as web service calls, and that you can easily generate code for a tag rather than for an expression. For example, in Dreamweaver you can drag a method of an object or web service into your code, and it generates a CFINVOKE tag for you. That's just a guess, though.
Dave Watts, CTO, Fig Leaf Software
Copy link to clipboard
Copied
Did your sodas stay safe, Dave?
Using BKBK's script, I got this:
createobject execution time: 30
cfinvoke execution time: 20
The CFC just did a select from a tiny database and cfdumped the results, so it's not much of a load.
This was CF9 and MySQL on Ubuntu 10.10 (my local machine).
cfinvoke seems to be faster, but perhaps it hogs more resources?
Interesting!
Copy link to clipboard
Copied
If you want a more valid test, write a cfc function that sets a variable and returns void. Then run the function each way a million times or so and see if there is any difference. I suspect there won't be, but I haven't tried it myself.
Copy link to clipboard
Copied
Now you should modify the script to make the same function call 1000 times, but with the createObject() version, create the object once, then call only the method on that instantiated object the 1000 times and see what you time difference is.
Personally, I prefer createObject() and my understanding is that if used properly will cause you no performance loss over cfinvoke and could provide performance gains in many operations over cfinvoke. It's also, in my opinion, much easier to look at and more portable to cfscript syntax.
Jason
Copy link to clipboard
Copied
Regarding:
Now you should modify the script to make the same function call 1000 times, but with the createObject() version, create the object once, then call only the method on that instantiated object the 1000 times and see what you time difference is
That would not be comparing createObject to cfinvoke.
Copy link to clipboard
Copied
Regarding:
Now you should modify the script to make the same function call 1000 times, but with the createObject() version, create the object once, then call only the method on that instantiated object the 1000 times and see what you time difference is
That would not be comparing createObject to cfinvoke.
Yes, but it would be demonstrating what the OP's instructor was most likely on about. IE; it'll demonstrate the overhead of creating a transient object every time, as opposed to using a persistent object.
It's all well and good demonstrating there's no real difference between the syntactical constructs when doing a like-for-like test, but it's more significant to demonstrate to one's self why one should not be using transients all the time if one doesn't need to.
--
Adam
Copy link to clipboard
Copied
Exactly. This is the point I was originally getting at.
Dave Watts, CTO, Fig Leaf Software
Copy link to clipboard
Copied
Using BKBK's script, I got this: createobject execution time: 30
cfinvoke execution time: 20
Running a test like that once is meaningless (and BKBK does allude to this, so I'm not griping at their suggestion). Also, the tests should not hit an external resource (like a DB) because you don't have control over what that external system is doing. In the case of making a DB call, the first call might need to compile the execution plan for the SQL, which has a statistically significant overhead in a test like this, plus there's a good chance the resultset will be cached. So the second call will not be doing the same work that the first call needs to make. Also making / using / closing the DB connection is going to be a very high percentage of the execution time here, so this dilutes the accuracy of the timing of the thing you're actually wanting to test.
What you need to do to performance test some code is to run the tests separately (so don't have "test a" and then "test b" in the same request), they need to have a ramp-up period - allowing for code to be compiled, etc - and they need to be run repeatedly and under load (like with JMeter). Note: I know you are not saying what you've done is authoritative, but it's important to make sure people don't get the idea that this sort of simple test is a good approach to testing this sort of thing.
On the other hand, it can be a quick way to find issues. If you were timing two bits of code, and one was 10ms and one was 2000ms, and remained so after a coupla retests, then I'd say the result is probably indicative of something; however the difference between 20-30ms is statistically insignificant here. If you ran separate tests 100 times, and the results averaged out like that, I'd probably conclude there might be a consideration though.
Testing and benchmarking is interesting stuff. Kinda.
That said, I doubt there's ever going to be a significant consideration when comparing two language constructs that do the same thing, so I'd really never bother worrying about this sort of thing. Tuning DB queries or watching how big data structures get is always going to be a better place to look for performance gain. So in regards to <cfinvoke> vs createObject(), I think how well either approach fits in with the existing code base and the knowledge of the developers working on the code is a more significant consideration. I also would not go changing existing code that is already working (not that you're suggesting you are), because that will add more dev, QA and UAT re-testing overhead than any performance gains would ever accumulate to.
--
Adam
Copy link to clipboard
Copied
BreakawayPaul wrote:
Did your sodas stay safe, Dave?
Using BKBK's script, I got this:
createobject execution time: 30
cfinvoke execution time: 20
The CFC just did a select from a tiny database and cfdumped the results, so it's not much of a load.
This was CF9 and MySQL on Ubuntu 10.10 (my local machine).
cfinvoke seems to be faster, but perhaps it hogs more resources?
Interesting!
Interesting indeed! I, too, was surprised to get
createobject execution time: 47
cfinvoke execution time: 0
Your instructor is apparently worth his salt (even if only for letting us realize there could be a significant difference between the two).
Copy link to clipboard
Copied
Another thought: the test will be cleaner with distinct names for the respective return variables. This avoids the extra factor of one variable overwriting another.
<cfset t1=getTickCount()>
<cfinvoke component="Test" method="testFunction" returnVariable="var1">
<cfset t2=getTickCount()>
<cfset t3=getTickCount()>
<cfset var2 = CreateObject("component","Test").testFunction()>
<cfset t4=getTickCount()>
<cfoutput>
cfinvoke execution time: #t2-t1#<br>
createobject execution time: #t4-t3#
</cfoutput>
This time I got
cfinvoke execution time: 0
createobject execution time: 15
Incidentally, I am on ColdFusion 9.0.1.274733
Copy link to clipboard
Copied
But it's a meaningless test. If one ran it again, then one could quite likely get the opposite result. Or it might have been 200ms / 0ms (if a GC was going on at the time of the first measurement, or the PC happened to be doing something else at the time). So running that sort of test and publishing the results as if you've acheived something is just daft.
At a minimum, one needs to run a lot of iterations to reduce the impact of outlying results or other inate fluctuations in processing. But - as I said - even looping over a test case and averaging the results is giving a false impression because it's not representative of how code is run in a production environment, so does not take into consideration any other contributing factors, some of which could be significant.
However... to demonstrate there is unlikely to be anything worth considering here, I wrote an iterative test, as follows:
C.cfc
<cfcomponent>
<cffunction name="echo">
<cfargument name="message" required="true">
<cfreturn arguments.message>
</cffunction>
</cfcomponent>
test.cfm
<cfparam name="URL.method" type="regex" pattern="^(?i)(?:createobject|cfinvoke|cfinvokeargument)$">
<cfparam name="URL.iterations" type="integer" default="0">
<cfset iTotal = 0>
<cfif URL.iterations GT 0>
<cfloop index="i" from="1" to="#URL.iterations#">
<cfset sMessage = "Hello World @ #now()#">
<cfswitch expression="#URL.method#">
<cfcase value="createobject">
<cfset iBefore = getTickCount()>
<cfset sResult = createObject("C").echo(message=sMessage)>
<cfset iAfter = getTickCount()>
</cfcase>
<cfcase value="cfinvoke">
<cfset iBefore = getTickCount()>
<cfinvoke component="C" method="echo" message="#sMessage#" returnvariable="sResult">
<cfset iAfter = getTickCount()>
</cfcase>
<cfcase value="cfinvokeargument">
<cfset iBefore = getTickCount()>
<cfinvoke component="C" method="echo" returnvariable="sResult">
<cfinvokeargument name="message" value="#sMessage#">
</cfinvoke>
<cfset iAfter = getTickCount()>
</cfcase>
</cfswitch>
<cfset iRuntime = iAfter - iBefore>
<cfif NOT structKeyExists(variables, "iMinimum")>
<cfset iMinimum = iRuntime>
<cfelseif iRuntime LT iMinimum>
<cfset iMinimum = iRuntime>
</cfif>
<cfif NOT structKeyExists(variables, "iMaximum")>
<cfset iMaximum = iRuntime>
<cfelseif iRuntime GT iMaximum>
<cfset iMaximum = iRuntime>
</cfif>
<cfset iTotal += iRuntime>
</cfloop>
<cfoutput>
Minimum: #iMinimum#ms<br />
Maximum: #iMaximum#ms<br />
Mean: #iTotal / URL.iterations#ms<br />
</cfoutput>
<cfelse>
Test not run<br />
</cfif>
This performs one of three tests:
* using createObject()
* using <cfinvoke> with inline arguments
* using <cfinvoke> with <cfinvokeargument>
It allows one to control the number of iterations run, and tests as little as possible other than the invocation overhead (ie: the method being called doesn't do much).
Over repeated test runs of 10000 iterations, all tests came back with an average of around 0.5ms.
As I said before, this is still a mostly pointless test as it does not emulate a real-world situation, but it's orders of magnitude better than running one iteration and trying to infer something from that.
The bottom line is:
* there is no issue;
* object creation & discard is very very fast, and differences in invocation methods are not going to be a sensible place to start tuning one's code.
What might be an interesting test to perform (although would need to be under parallel load to make it more than meaningless) would be to check how memory is assigned / freed by these operations. That's probably a more significant metric - if there are differences - than how long things take. I'll leave it to someone else to test that (although I hasten to add I still think it's a fool's errand).
--
Adam