Viewing By Entry / Main
July 14, 2004
Which should you use? If you're using CFMX then all scopes are structs and you will be much safer with structKeyExists(scopeName,"varName"). Depending on your JVM, it may also be faster than isDefined("scopeName.varName").
isDefined() tries very hard to find any possible definition of the requested variable. It searches through the scopes as if the variable name is not scope-qualified - even when it contains a dot (.) which seems to date back to pre-MX ColdFusion which allowed dots in variable names. For example, if you say:
<cfset variables.variables.foo = 42 />
you may be surprised to discover that isDefined("variables.foo") is YES, even tho' there is no variable called foo in the variables scope. If you say isDefined("foo") and foo is a URL scope variable, you'll get YES (unqualified scope lookup) but if you say isDefined("x.foo") you'll get NO even if x.foo is passed in the URL - you need isDefined("url.x.foo") for that. Buggy? Perhaps. Confusing? Certainly!
In all cases, structKeyExists() lets you test for variable existence much more precisely:
structKeyExists(variables,"foo") is NO
structKeyExists(variables,"variables") is YES
structKeyExists(variables.variables,"foo") is YES
(for the examples above).

Comments

so, is there any plans to rectify this "bug" in future versions, or should we concede to using structkeyexists() for all of our isDefined() needs? thats a lot of code to rework, although i never ask if a variable is defined that could be questionable, the speed issue is one thing to consider.


And what about the arguments scope? In my CFCs I noticed I have to use isDefined('arguments.myArg') instead of structKeyExists(arguments, 'myArg'): sometimes structKeyExists doesn't work for me.


I've never had to use isDefined() with arguments. Can you provide a concrete example that shows the problem?


Sean,<br /><br />More of the same please.<br /><br />I've often wonder why I should "choose one over the other" with several CF tags, functions and techniques. I've rejected converting my if "x" is "y" to comparenocase on the grounds that the comparenocase logic does my head in and all the "speed" justification examples seem to be over 10,000 loops ;-)


I've never had a problem with isDefined. If you follow "good practice" by not naming structures the same as scope names, you will save yourself a lot more trouble than this particular "bug".


CFMX accepts and recognizes periods in variables. Therefore: <br /> works fine even if it is bad coding practice, and Isdefined("test.foo") or IsDefined("variables.test.foo") both return true as they should. StructKeyExist(variables,"test.foo") incorrectly returns false.<br /><br />As for performance, why would Isdefined("Form.Field") search all scopes when I have specificly specified a valid CF scope (Form). I can understand if I do not include a valid scope why it would search them all, but there are times that may be a desired action.


I've had some very specific problems with isDefined() accessing the CGI scope (because its argument was not defined in the variables scope or arguments scope) - accesses to the CGI scope can cause callbacks to the web server :(<br /><br />That's my main reason for avoiding isDefined() - the fine-grained control over which scope to search is important too, performance is secondary.


What are your thoughts on the isNotMap() function. I found this function in the Programming ColdFusion MX book by Rob Brooks-Bilson.


I've never heard of that function Laurence...


OK, here's the same thing escaped so that it actually shows up:<br /><br />&lt;cfif IsDefined(&quot;SESSION.Username&quot;)&gt;<br />&nbsp;&nbsp;&lt;!---secure stuff here ---&gt;<br />&lt;/cfif&gt;


I think I discovered a 'good' bug if there is such a thing. I worked on resolving an issue with our site where a bit of wayward development resulted in a variable in the form scope was called 347TID which is syntactically incorrect as it begins with a numeric character. IsDefined throws an error declaring the issue with the naming of the variable, where as structKeyExists permits it and returns true or false. Not necessarily a vote in favour of isDefined, but definately something that needs fixing in structKeyExists???


@David, no, that's exactly how ColdFusion is intended to work: you can put *any* key you like in a struct: form["347TID"] and structKeyExists() must work with that.


Thanks.Is there any rationale for this? And what is the difference between 347TID being a syntactically incorrect variable name but at the same time being a permitted struct key? Is variables.347TID both a syntactically incorrect variable name and a permitted struct key at the same time? I am truly interested in understaning how and why this is the case. There's something similar to the Heisenberg uncertainty principle going on here ;-)


@David, *anything* is a valid struct key - any string. That's basic ColdFusion stuff. It has nothing whatsoever to do with variable names. I'm not sure how to explain it any more clearly...


I understand *anything* can be a struct key. I didn't know that after 8 years of CF but hey I do now. What I don't understand is why this is the case for struct keys when it isn't the case for variables; I can't see any reason for this difference. Isn't a variable actually a key in the variables scope struct? Anyway no matter. I think we might be talking at cross purposes.


"StructKeyExist(variables,"test.foo")" -- Wouldn't this be written: StructKeyExist(variables.test,"foo") ?


@Ryan, I assume you're answering Larry's comment? Yes, isDefined("variables.test.foo") is technically equivalent to:

structKeyExists(variables,"test") and structKeyExists(variables.test,"foo")

You need both.


hum...okay, played around with that a bit more as our dev group is trying to decide what REALLY is best practise....

Short answer: structKeyExists is SLOWER and has no advantage other than forcing coder to use scoping, which s/he should do anyway...

Long Answer:

whereas for the examples below url.pageid is a real url variable

STRUCTKEYEXISTS: structKeyExists is pretty much the same speed no matter what you throw at it, keys can be real or not (url, 'pageid' and url, 'pageadfasdf' are about the same, no big diff one way or the other) and if you throw a scope at it that does not exist, it throws, so it forces you to be careful about scoping.

ISDEFINED: isDefined is super fast when the scope exists and you pass in a scope (url.pageid), however, it is extremely extremely mega super slow when you pass in a non-existant scope (urlss.pageid). it is also super super mega slow if you pass in a non-scoped non-existant variable (pagelksjdljsdf) it is quite fast, on average 10 to 20% faster than structKeyExists, when you pass in a SCOPED variable, existant or not (url.pageid or url.aoiausldkjf)

So what does that mean in real life? Well, as long as you are asking for a SCOPED variable that is extant, and you don't mispell the scope (url for instance), isDefined will be faster on average.... If you are looking for a NON-SCOPED variable, you have no choice but to live with isDefined.... hum....which can be very very slow when the variable does not exist. If you want to make sure you scope all your vars, use structKeyExists and live with a small speed hit, but be assured it is scoped to a real scope (or throw an error). So for me, in real life use, that means isDefined is probably the better choice as it is overall going to be faster as long as i can spell my scope correctly. It also is the only choice to use when looking for multiple scope variables such as FORM.ID vs URL.ID and your page/function can take either and want to simply ask isDefined('ID') but if you want to prioritize one over the other (which you probably should) then you can again use either define/struct where you have a little bit of script that checks each (form.id and then url.id) and assigns a variables.myID which is then passed in to page/function to use.... again, this would indicate isDefined would be preferred as it would be faster for that call too, as long as it is scoped and the scope exists. So I can't find any reason to use structKeyExists other than it might seem like better code because it forces scoping.... How sad... it seemed like it should be faster as it specifies the scope....

here is sample code for you to run and see for yourself.... i'd love to know if you find anything different than what I did by platform or other (I am using CFMX702 on a WINDOWS platform). Is linux version any faster? Anyone know what actually happens at the compiled java level?)

<CFSCRIPT> ITERATIONS = 100000; writeoutput('ITERATIONS : ' & ITERATIONS & '<BR>' ); i=0; j=0; k=0; ttt1 =0; ttt2 =0; t1c =0; t2c=0;

for(k; k lte ITERATIONS; k=k+1){

T1 = getTickCount(); for(i; i lte ITERATIONS; i=i+1){ if(isdefined('url.asdfpageid')){ //do nothing } } T2 = getTickCount(); TT1 = T2-T1; ttt1=ttt1+TT1;

T3 = getTickCount(); for(j; j lte ITERATIONS;j=j+1){ if(structKeyExists(url, 'pageeid')){ //do nothing } } T4 = getTickCount(); TT2 = T4-T3; ttt2=ttt2+TT2; if(TT1 gt 0){t1c = t1c+1;} if(TT1 gt 0){t2c = t2c+1;}

if(TT1 gt 0 or TT2 gt 0){writeoutput('Iteration K: ' & k & ' isDefined: ' & TT1 & 'ms'); writeoutput(' structKeyExists: ' & TT2 & 'ms<br>');}

}

writeoutput('<BR>Times isDefined used time: ' & t1c); writeoutput('<BR>Times structKeyExists used time: ' & t2c);

writeoutput('<BR>FINAL isDefined: ' & TTT1 & 'ms'); writeoutput('<BR>FINAL structKeyExists: ' & TTT2 & 'ms');

</CFSCRIPT> <br>

<!--- The cfchart ---> <cfchart format="flash" xaxistitle="function" yaxistitle="Loading Time"> <cfchartseries type="bar" serieslabel="isDefined"> <cfchartdata item="isDefined" value="#variables.TTT1#"> </cfchartseries> <cfchartseries type="bar" serieslabel="structKeyExists"> <cfchartdata item="structKeyExists" value="#variables.TTT2#"> </cfchartseries> </cfchart>


@William, I ran your code - repeatedly - and the results always showed structKeyExists() being 20-30% FASTER than isDefined() on an Intel-powered Mac.


That is very very interesting. I suspect people aren't making false claims about it really being faster without doing some testing. I was trusting it was, until I came across a British blog that said the contrary, so I ran my own tests to verify. Sure enough, on my windows XP Pro sp2 CoreDuo 6400 2.30 4gb ram IIS5.1 cmfx702 with debug on, isDefined IS consistently slower. I am going to run the same code on our live server (windoze 2003 web edition dual xeon 2.8 IIS6 2gb ram cfmx702) and see what happens. If this is the case, that it is an environment issue more than a function issue, that might also imply that general optimization of jrun on a linux envronment is better than the windows equivalent. I would actually love to find that coldfusion code (esp the best practise versions of various functions) is in fact faster in linux environments (and thus overall performance should be faster) as I had originally wanted to push for setting up our live in that environment and plan on moving towards that platform in the mid- to long term because of the windows ram limitation in their web edition.


hum.. running on live server (dual xeon 2.8ghz 2gb ram windows 2003 web edition server)

2 sets of runs, 15 run throughs, not a single instance of structKeyExists as faster...

i'll post the code and results in two following posts.


updated code:

<CFSCRIPT> ITERATIONS = 1000000; writeoutput('ITERATIONS : ' & ITERATIONS & '<BR>' ); i=0; j=0; k=0; ttt1 =0; ttt2 =0; t1c =0; t2c=0;

for(k; k lte ITERATIONS; k=k+1){

T1 = getTickCount(); for(i; i lte ITERATIONS; i=i+1){ if(isdefined('url.pageid')){ //do nothing } } T2 = getTickCount(); TT1 = T2-T1; ttt1=ttt1+TT1;

T3 = getTickCount(); for(j; j lte ITERATIONS;j=j+1){ if(structKeyExists(url, 'pageid')){ //do nothing } } T4 = getTickCount(); TT2 = T4-T3; ttt2=ttt2+TT2; if(TT1 gt 0){t1c = t1c+1;} if(TT2 gt 0){t2c = t2c+1;}

if(TT1 gt 0 or TT2 gt 0){writeoutput('Iteration K: ' & k & ' isDefined: ' & TT1 & 'ms'); writeoutput(' structKeyExists: ' & TT2 & 'ms<br>');}

}

writeoutput('<BR>Times isDefined used time: ' & t1c); writeoutput('<BR>Times structKeyExists used time: ' & t2c);

writeoutput('<BR>FINAL isDefined: ' & TTT1 & 'ms'); writeoutput('<BR>FINAL structKeyExists: ' & TTT2 & 'ms');

if(TTT1 GT TTT2){ writeoutput('<BR>structKeyExists is faster by: ' & TTT1-TTT2 & 'ms which is ' & NUMBERFORMAT((TTT1-TTT2)/TTT1*100, '9.9') & '% faster'); }else{ writeoutput('<BR>isDefined is faster by: ' & TTT2-TTT1 & 'ms which is ' & NUMBERFORMAT((TTT2-TTT1)/TTT2*100, '9.9') & '% faster' ); }

</CFSCRIPT>

<br>

<!--- The cfchart ---> <cfchart format="flash" xaxistitle="function" yaxistitle="Loading Time"> <cfchartseries type="bar" serieslabel="isDefined"> <cfchartdata item="isDefined" value="#variables.TTT1#"> </cfchartseries> <cfchartseries type="bar" serieslabel="structKeyExists"> <cfchartdata item="structKeyExists" value="#variables.TTT2#"> </cfchartseries> </cfchart>


15 runs where scope exists, but key does not (url.pageid) with 1,000,000 iterations of 2 sets of 1,000,000 calls

Times isDefined used time: 107 Times structKeyExists used time: 118 FINAL isDefined: 4925ms FINAL structKeyExists: 5641ms isDefined is faster by: 716ms which is 12.7% faster

Times isDefined used time: 123 Times structKeyExists used time: 136 FINAL isDefined: 5255ms FINAL structKeyExists: 5626ms isDefined is faster by: 371ms which is 6.6% faster

Times isDefined used time: 104 Times structKeyExists used time: 138 FINAL isDefined: 5792ms FINAL structKeyExists: 6002ms isDefined is faster by: 210ms which is 3.5% faster

Times isDefined used time: 148 Times structKeyExists used time: 150 FINAL isDefined: 6221ms FINAL structKeyExists: 6375ms isDefined is faster by: 154ms which is 2.4% faster

Times isDefined used time: 349 Times structKeyExists used time: 318 FINAL isDefined: 21747ms FINAL structKeyExists: 23098ms isDefined is faster by: 1351ms which is 5.8% faster

Times isDefined used time: 271 Times structKeyExists used time: 325 FINAL isDefined: 14802ms FINAL structKeyExists: 31557ms isDefined is faster by: 16755ms which is 53.1% faster

Times isDefined used time: 300 Times structKeyExists used time: 293 FINAL isDefined: 25496ms FINAL structKeyExists: 35296ms isDefined is faster by: 9800ms which is 27.8% faster

Times isDefined used time: 333 Times structKeyExists used time: 271 FINAL isDefined: 22676ms FINAL structKeyExists: 26463ms isDefined is faster by: 3787ms which is 14.3% faster

Times isDefined used time: 245 Times structKeyExists used time: 268 FINAL isDefined: 11288ms FINAL structKeyExists: 15934ms isDefined is faster by: 4646ms which is 29.2% faster

Times isDefined used time: 220 Times structKeyExists used time: 267 FINAL isDefined: 11470ms FINAL structKeyExists: 16012ms isDefined is faster by: 4542ms which is 28.4% faster

Times isDefined used time: 266 Times structKeyExists used time: 281 FINAL isDefined: 10572ms FINAL structKeyExists: 13946ms isDefined is faster by: 3374ms which is 24.2% faster

Times isDefined used time: 278 Times structKeyExists used time: 275 FINAL isDefined: 10125ms FINAL structKeyExists: 13329ms isDefined is faster by: 3204ms which is 24.0% faster

Times isDefined used time: 242 Times structKeyExists used time: 253 FINAL isDefined: 10067ms FINAL structKeyExists: 14494ms isDefined is faster by: 4427ms which is 30.5% faster

Times isDefined used time: 192 Times structKeyExists used time: 221 FINAL isDefined: 7526ms FINAL structKeyExists: 12226ms isDefined is faster by: 4700ms which is 38.4% faster

Hum...on my random pulls, i usually see about 1 in 10 where structKeyExists is faster, but this particular series (ran in 5 windows 3 times each concurrently, thus under some load), I didn't get any...

15 pulls where scope and key exist.

Times isDefined used time: 312 Times structKeyExists used time: 317 FINAL isDefined: 7706ms FINAL structKeyExists: 8730ms isDefined is faster by: 1024ms which is 11.7% faster

Times isDefined used time: 311 Times structKeyExists used time: 338 FINAL isDefined: 8596ms FINAL structKeyExists: 17218ms isDefined is faster by: 8622ms which is 50.1% faster

Times isDefined used time: 326 Times structKeyExists used time: 314 FINAL isDefined: 12959ms FINAL structKeyExists: 16560ms isDefined is faster by: 3601ms which is 21.7% faster

Times isDefined used time: 301 Times structKeyExists used time: 275 FINAL isDefined: 12339ms FINAL structKeyExists: 16680ms isDefined is faster by: 4341ms which is 26.0% faster

Times isDefined used time: 298 Times structKeyExists used time: 322 FINAL isDefined: 12346ms FINAL structKeyExists: 17918ms isDefined is faster by: 5572ms which is 31.1% faster

Times isDefined used time: 350 Times structKeyExists used time: 347 FINAL isDefined: 12619ms FINAL structKeyExists: 17863ms isDefined is faster by: 5244ms which is 29.4% faster

Times isDefined used time: 358 Times structKeyExists used time: 331 FINAL isDefined: 12248ms FINAL structKeyExists: 18148ms isDefined is faster by: 5900ms which is 32.5% faster

Times isDefined used time: 374 Times structKeyExists used time: 363 FINAL isDefined: 9712ms FINAL structKeyExists: 12739ms isDefined is faster by: 3027ms which is 23.8% faster

Times isDefined used time: 320 Times structKeyExists used time: 355 FINAL isDefined: 15134ms FINAL structKeyExists: 19300ms isDefined is faster by: 4166ms which is 21.6% faster

Times isDefined used time: 314 Times structKeyExists used time: 324 FINAL isDefined: 14377ms FINAL structKeyExists: 19059ms isDefined is faster by: 4682ms which is 24.6% faster

Times isDefined used time: 291 Times structKeyExists used time: 349 FINAL isDefined: 13776ms FINAL structKeyExists: 21890ms isDefined is faster by: 8114ms which is 37.1% faster

Times isDefined used time: 360 Times structKeyExists used time: 351 FINAL isDefined: 13814ms FINAL structKeyExists: 23769ms isDefined is faster by: 9955ms which is 41.9% faster

Times isDefined used time: 244 Times structKeyExists used time: 247 FINAL isDefined: 9884ms FINAL structKeyExists: 14090ms isDefined is faster by: 4206ms which is 29.9% faster

Times isDefined used time: 293 Times structKeyExists used time: 266 FINAL isDefined: 9830ms FINAL structKeyExists: 13967ms isDefined is faster by: 4137ms which is 29.6% faster

Times isDefined used time: 312 Times structKeyExists used time: 315 FINAL isDefined: 8854ms FINAL structKeyExists: 11584ms isDefined is faster by: 2730ms which is 23.6% faster

hum... well... not sure what to say. even worse than what i get on my development machine.


I ran it multiple times. structKeyExists() was ALWAYS faster. The closest margin was 5.5%. More typically, structKeyExists() was around 15-20% faster on this latest code. Example run:

Times isDefined used time: 786 Times structKeyExists used time: 746 FINAL isDefined: 2278ms FINAL structKeyExists: 1790ms structKeyExists is faster by: 488ms which is 21.4% faster


I would have to agree with William I get the same results as he does. The few that structKeyExits is faster it is a very small margin yet when is defined is faster it is by a fair amount.


I also get the same result as william and was surprised to see the isdefined was faster. I just did a basic look 10,000 times with a cfif structKeyexists / isdefined in it.


@Jeff, @William, @Jake - it must be due to JVM differences then. A good reason why low-level code optimizations are unreliable (and generally not worth the effort).

As I said in several places, I've had specific problems with isDefined() so I avoid it - except in the one case where I've needed it: for testing against null in the result of a method call:

var res = obj.someMethod(args);

if ( isDefined('res') ) ...

Currently there's no other way to test for null results (in ColdFusion, at least).


So why would this:

<cffunction name="onRequest" returntype="boolean"> <cfargument name="targetPage" type="string" required="true" /> <cfif isUserLoggedIn()> <cfif not structKeyExists(session,"user")> <!--- do something ---> <cfinclude template="test.cfm"> <cfelse> <cfinclude template="#arguments.targetPage#"> </cfif> </cfif> <cfreturn true /> </cffunction>

behave so differently from this:

<cffunction name="onRequest" returntype="boolean"> <cfargument name="targetPage" type="string" required="true" /> <cfif isUserLoggedIn()> <cfif not isDefined(session.user)> <!--- do something ---> <cfinclude template="test.cfm"> <cfelse> <cfinclude template="#arguments.targetPage#"> </cfif> </cfif> <cfreturn true /> </cffunction>


@Mark, first, I assume you mean isDefined("session.user") - with quotes?

Second, you don't say how those two pieces of code behave "so differently" so it is hard to tell what you're asking...


Post Your Comments
Name:
Email Address:
Comments
*** Please note that all comments require moderation so it may be some time before your comment posts to this blog! ***
Remember My Information:
 



Hosting provided by