Friday, 30 August 2013

Bug watch: 212206 untriaged ColdFusion 10 bugs

G'day:
The code & testing for the article I'm writing is going to take a few hours, and Mon-Fri I only have a coupla hours available each day to write said code, so I'll not get to the actual article until the weekend. In the meantime... where are Adobe at with bug fixing?

A while back I posted an article "212 untriaged ColdFusion 10 bugs", and a follow-up a few weeks later "Follow-up to ~".

As of today, there are 206 untriaged bugs raised against ColdFusion 10. This is a net reduction of six since May, three months ago.

Thursday, 29 August 2013

Most popular unhandled ColdFusion bugs

G'day:
Apropos of not much, I grabbed a list of all the ColdFusion bugs in the bugbase that:
  • have ten or more votes;
  • do not have a status of "fixed".
Here's a table of them, and my thoughts.

Cool Railo thing I learned at the London Railo Group meeting last night

G'day:
Last night was the first meeting of the London Railo Group. A bunch of people in the local London Railo community got together at the Pixl8 offices and had an informal catch-up and drank beer and ate pizza. There was only about a dozen people there, but I think it was a successful first meeting. Well done Alex Skinner for organising it, and Mark Drew for giving an impromptu presentation on some random Railo stuff he finds interesting.

Wednesday, 28 August 2013

ColdFusion guessing at query column types

G'day:
I've been aware of this for a while, but I've only just got around to investigating it. ColdFusion guesses column types of queries when doing QoQ.

CFML: Referencing a query column via dot notation or associative array notation

G'day:
A few days back I asked a question via a Survey Monkey survey, relating to people's awareness of some CFML syntax relating to query columns.

The question was this:
What values for "dotNotation" and "arrayNotation" does this code return:

<cfscript>
q = queryNew("");
queryAddColumn(q, "id", "varchar", ["a","b","c","d","e"]);

writeDump({
    dotNotation        = isArray(q.id),
    arrayNotation    = isArray(q["id"])
});
</cfscript>
(NB: originally the values in the query column were 1-5, but as there was some question about values being treated as booleans, I changed that (the values were irrelevant to the question)).

Monday, 26 August 2013

CFML: parameterStillExists()

G'day:
This is an adjunct to my preceding article, pretty much allowing me go say "One word: parameterExists()" without actually qualifying why I said that that without requiring more than one word.

Most CFMLers probably know there is a function parameterExists() in CFML. Most people will know it's poor form to use it in favour of isDefined(), and really it's poor form to use isDefined() rather than structKeyExists(). Some people will be surprised to hear parameterExists() is still in the language; others will be aware that it's deprecated (and, curse them, others still will think that because it's deprecated that it will no longer work, but I've already covered that).

Now I knew parameterExists() had been deprecated for ages, and I always cite it as having been deprecated for over a decade, and use it as an example of why people should not get all uppity and hand-wringy at the notion of things in CFML being deprecated. We could happily deprecate <cfset> and still expect it to work in ColdFusion 15, in ten years time (no, I neither expect there to be a CF15, nor for anyone to be discussing CFML in ten years time. It was an example).

Anyway, I decided to find out how long it's been deprecated. Up until now I thought it was since CF5, because I have a copy of the CF5 docs, and it's already deprecated there:

For Pete's sake: "deprecated" does not mean "removed"

G'day:
One thing I hate (of a very long list, it seems...) is people simply not getting what the notion of "deprecation" means. In the context of software (~ features) I mean.

For goodness sake: if one has a software system and one declares an element of that system to be "deprecated", this does not mean it's been removed from the system, or will now break, or will work in any way different from how it did before it was marked deprecated.

If we're talking about ColdFusion, and we were to say that <cfform> (pseudo-random example, but a frickin' good candidate for deprecation!) was deprecated as of CF11... then it would still work exactly the same as it does in CF10 in CF11 and every subsequent release of ColdFusion until it was finally retired.

All deprecation means is to flag that a feature will no longer receive enhancement (or bug fixing, etc), and that it will possibly be removed from the product at some later date. I would go as far as to say that if one was being professional about things, then simply saying in the release notes of CF11 that <cfform> was deprecated (but no mention of when it would be removed) then that's almost a guarantee that it'll still be there in CF12 as well. Deprecation is one thing, but the decision to actually remove something from the language ought to be flagged 1-2 version out (something that Adobe has never done).

The process of creating a clear repro case for a bug: <cfchart> vs Async CFML gateways

G'day:
It's bank holiday Monday and it's gloriously sunny outside, but for some reason I am investigating a bug I didn't even know existed nor would ever be likely to encounter. I'm a dick.

Friday, 23 August 2013

Kenya

G'day:
My mate Duncan did some research for me on the subject of date formats, as feedback to yesterday's article "Not every single string that can possibly be parsed as a date should be treated like a date!".

I had made this claim:
[...]there is not situation in which a string formatted as "xx/xx/xx" should be interpreted as a date "yy/mm/dd". No-one writes dates as "yy/mm/dd".

Well Duncan pointed me at this Wikipedia page: "Date format by country". Wikipedia seems to have pages for everything.

As it turns out there is a country in which the date format is "yy/mm/dd": Kenya. Who'd've known?

That said, I stand by my point. Not for the sake of pigheadedness (well not solely that ;-), but because "Kenyan" (or any approximation thereof) is not a locale that ColdFusion supports (according to the locales listed in server.coldfusion.supportedLocales, anyhow).

So in the context of CFML... there still really is no justification for ColdFusion to ever to consider the string "xx/xx/xx" as a date-parseable value representing the format "yy/mm/dd". For it to do so, IMO, is a bug: 3617365.

--
Adam

Wednesday, 21 August 2013

CFML: What's the truth?

G'day:
This might be painfully obvious to everyone, but I had a "what the hell?" moment this morning when having a look at some of Aurel's code posted against one of my earlier articles. It turns out I have the wrong expectations as to what and when CFML considers values to be boolean.

I've gotta be quick as it's taken most of my lunch break to distill the code down to something simple, but here's the crux of the matter:

truths = [-2,-1,1,2,true,'"yes"'];

for (A in truths){
    test(A, true);
    for (B in truths){
        same = A==B;
        bothNumeric = isNumeric(A) && isNumeric(B);
        expression =
        test("#A# EQ #B#", same || !bothNumeric);
    }
    writeOutput("<hr>");
}

function test(expression, expected){
    var result = evaluate("#expression# ? true : false");
    var colour = result == expected ? "green" : "red";

    writeOutput('<span style="color:#colour#">#expression#: #result#</span><br>');
}

Do you like my - fairly legitimate, I think? - use of evaluate() in there? ;-) But that's an aside. Here's the output:

Is a query column an array?

G'day:
Can you pls help me gather some code-awareness metrics by answering this very quick, single-question survey: http://www.surveymonkey.com/s/HHV2TQQ

As per the question text, it's not a trick question, and I'm not after you working out "the correct answer", I'm just wanting to know what you understood of the CFML in question.

If you have an online presence and can circulate this, that'd be helpful too: the more answers the merrier.

Thanks for helping out.


--
Adam

Brad Wood makes an interesting feature suggestion for CFML

G'day:
OK, having got today's Stack Overflow rant out of the way, here's the article I meant to write this morning. Or actually last night. Well actually "here's the text that someone else wrote that I meant to copy and paste in lieu of actually writing something". Here it is. CTRL-C, CTRL-V...

Cocks: or "the Stack Overflow community" as they perhaps prefer to be known

G'day:
F*** me Stack Overflow is populated by cocks. I was amidst answering someone's CFML question when an insecurity of self-appointed moderators (I have decided "insecurity" is an apt collective noun for these people) closed the question underneath me, so I could not post the answer.

Tuesday, 20 August 2013

A very uncontroversial survey from Adobe regarding ColdFusion deprecation

G'day:
I almost got "excited" about some forward thinking from Adobe here. Rakshith just posted this on Twitter:

Take this #ColdFusion survey to let #Adobe know features that need to be deprecated/retained/replaced https://www.surveymonkey.com/s/XCKLJXH
Twitter
I thought "woohoo! let's get rid of some cruft". And you should go fill it out.

CFML: evaluate(): CFMX7 and CF10 compared

G'day:
A few days ago I had a look at this - apparent - myth that evaluate() is terribly terribly slow and one should not use it on that basis alone (not forgetting there's no real justification to use it these days anyhow). I tested on ColdFusion 9 and 10 to gather my results (the final results posted were from CF10, but the two were much of a muchness). I speculated that given there was not really good-enough evidence to support these performance issue claims in modern versions of ColdFusion that perhaps the problem existed "back in the day" and it's since been remedied. So over the weekend I blew the dust off my CFMX7 install, and ran the tests again on a like-for-like config, alongside re-running the tests on CF10. And for the sake of completeness, here are the results (they should be read in conjunction with the original article, as I do not go into great detail as to what was being tested).

Monday, 19 August 2013

Railo docs licence weirdness?

G'day:
I just noticed this in the licence (yes, I read licences) for the new Railo docs site:

The Railo Docs Site by Mark Drew (@markdrew) is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

CFML: Tickets for those operators I mentioned

G'day:
This afternoon I posted an article suggesting some more operators than CFML might benefit from. At that stage I had not raised any bugbase tickets as I wanted to get a feel for what people thought before doing so.

I've had uniformly positive feedback, so I've raised tickets where necessary:

OperatorColdFusion ticketRailo ticket
<=>3614455RAILO-2582
==~3614456RAILO-2581
?.3614459RAILO-2579
??3589888Already implemented as a variant of ?:
.. / ...3614460RAILO-2579

Thinking about operators in CFML

G'day:
I was messing around with some array-building code the other day - I don't even recall what it was for now - and I thought if I want to concatentate two arrays, why do I need to do this:

lowNumbers = ["tahi", "rua", "toru", "wha", "rima", "ono"];    // the ones I remember
higherNumbers = ["whitu", "waru", "iwa", "tekau"];            // the ones I always have to look up

numbers = lowNumbers;
numbers.addAll(higherNumbers);

writeDump([lowNumbers,higherNumbers, numbers]);

Why can't I just do this:

numbers = lowNumbers + higherNumbers;

Damn you and your opinions, Dave Ferguson

G'day:
(Just kidding Dave... that's just a reference to Scott saying I'd be doing a blog article about you after hearing the latest CFHour: "Show #189 - Manage Your Indention").

This is clarification of something Dave did dwell on during that podcast, in reference to my article 'Really, Adobe: "NotWorthEffort"'. And to fulfill Scott's observation that I'd feed-back on what Dave said.

That article draws attention to what I perceive is slack-arse-ness on Adobe's part in how they dealt with a user-raised issue on the bugbase relating to the toScript() function (3041310).

The lads on CFHour made a few points / observations which are relevant, but at least partially tangential to the point I was trying to make.

Firstly: yeah, the code that toScript() generates works. It's slightly verbose, but it works. That's a very low bar to set though, isn't it? The approach taken here demonstrates a certain lack of finesse that one might expect from a function Adobe (OK: it was Macromedia) have provided to effect a better solution that just rolling one's own. And surely that's the intent here? It also kinda shows their hand in regards to their JavaScript capabilities, which seem to be "less than ideal" (I'd actually say: "not up to the job").

Saturday, 17 August 2013

CFML: A bit more on getFunctionCalledName(): it's broke in Railo

G'day:
Sorry for the "slow-news week" this week (well: it's been a coupla weeks now). Everything I'm planning to write up will take a while to prep, which means I find excuses to do something else instead.

A while back I wrote about getFunctionCalledName(), mostly because I can never find it in the ColdFusion docs as I never remember enough of what it's called to google it properly. So that article was purely google-bait (it didn't work, just in case you're interested... I still cannot ever remember what getFunctionCalledName() is called, nor can I successfully google it up).

Wednesday, 14 August 2013

Moan moan moan bloody moan

G'day:
Another point for Rob Glover... he reminded me about one of my pet hates yesterday... CFML code in which the dev has closed the tags. I wrote an article about closing CFML tags ages ago. But what else annoys me in my CFML world?

Here's a list of CFML- or dev-oriented pete peeves of mine. In no particular order. And why? Just for the hell of it, on a slow news day.

The idea that pointless closing  CFML tags is "a thing"

'nuff said.

Comments that simply state what the next line of code is doing

eg:

// create the object
theObject = createObject("TheComponent");

Seriously? F*** off if you do that. If a person is looking at that file, they understand CFML (or at least they should, and comments are not the way to solve that if it's not the case!), and reading the code is a fine way of identifying what the code does.

This is made worse by poorly maintained code in which the comments reflect the state of the code as it was a year earlier, but not how it is now.

Use comments to explain what's not already on the screen, and not readily inferrable from what's on the screen: why you're doing something, how an algorithm works, or an explanation of why unlikely-looking code needs to be the way it is (eg: some third-party service wanting dates as strings, or something).

Copying and pasting of code instead of refactoring

If you want the same functionality somewhere else in your code base: refactor, don't copy and paste (or as I term it in this context: "cu*t and paste". Because if you do it: you are one). I don't know how many times I've needed to fix something only to find out that the same bit of logic exists in several places in the code base.

Using tags in logic-only files

Tags are for interacting with mark-up. They are clumsy and clunky-looking when used in logic-only files. The only time they should be used in these cases is when Adobe have dropped the ball and there is no script-compatible implementation of the given functionality.

Not RTFMing (or should that be "RingTFM"?)

Asking questions (especially in a written forum) before even bothering to just look up the answer in the docs. Or via Google. It's just laziness, and people like that do not deserve the help they are soliciting.

CFML's "Wizard" tags

Like anything to do with the UI (<cfform>, <cfpod> etc), and including <cfinsert> and <cfupdate>. Have some pride: do your work properly.

Adobe's attitude to their clients

Their support of and participation in their developer community is appalling.

Not understanding when to use #

Thanks to David Nash for this one. Its not complicated (like when to use an apostrophe), yet so many people simply do'nt bother to learn the languages' syntax properly. NB: sic.

Not having a basic understanding of computing

Not understanding why floating point numbers are inaccurate, why data types have size limitations; or how Javascript runs on a client machine and ColdFusion runs on a server and the two don't interact directly, etc. I don't expect people to be able to write a C compiler (cue Sean to say something along the lines that he has ;-), or know the intricacies of the architecture of a CPU, but an understanding of the basics of the tool set one uses daily for one's career.

Being partisan about technology

Because I use Railo doesn't mean ColdFusion is automatically the villain (it's not for those reasons), or if I like CFML then PHP automatically sux, etc. Or I like Apple products therefore Microsoft products are automatically bad, etc. I know we work in IT, but not everything is binary. And you sound stupid if you hold opinions like that.

That's a random ten. There'll be others, and things that annoy me more, but happen less frequently so have not sprung to mind in the last 15min since I decided to write this.

I'm sure you'll disagree with at least some of these, or have some other ones of your own. Go on then...

--
Adam

Tuesday, 13 August 2013

London Railo Group

G'day:
Me mate Alex Skinner of Pixl8 is hosting the new London Railo Group. The first get together is on Weds Aug 28 - so two weeks' time - at their offices in Clapham.

I'm gonna pop along and take advantage of the beer and the wifi, and I think I saw mention of pizza somewhere (can't find the reference now). Oh, and having a yap about Railo and CFML and that sort of stuff.

If yer around, head over. Alex and the crew are cool bods (I've been working for / with them on and off for years, so know them well), and it'd be good to get the Railo community in London in touch with each other.

Hopefully see ya there.

--
Adam

Monday, 12 August 2013

"evalulate() is really slow". Is it now?

G'day:
I'm always the first person to screw up my nose when I see someone using evaluate() in any CFML written for versions of CF after CF5. But generally because it's an unnecessary function call as there's almost always native syntactical constructs which will do the job without dynamically evaluating a string. And also in the back of my mind there's that perennial notion that it's really slow. I'd not investigated this since I was using CFMX7, and never very rigorously. So I decided to have a look at it.

Firstly, there's a few common areas I see evaluate() being used. I think these were recommended approaches back in the pre-CFMX days, but they've been unnecessary since CF was revamped to run on Java.

Dynamic variable names

This is the most common one. It often stems from a form that has an iteration of form fields with numeric suffixes on the name attribute value, to kinda emulate an array. EG:

<input name="teamMember1">
<input name="teamMember3">
<input name="teamMember3">

And on the receiving end of the form post, we have this:

<cfloop index="i" from="1" to="3">
    <cfoutput>#evaluate("teamMember#i#")#<br></cfoutput>
</cfloop> 

This is shonky from the outset and the person has done themselves no favours by not scoping their variables. And had they scoped their variables, it might have occurred to them that evaluate() is simply not necessary here, and one can write much nicer code, thus:

form["teamMember#i#"]

Associative array notation will allow any variable to be accessed via a string containing its name, and obviously a string can be composed at runtime to contain dynamic values.

Working around the shortcomings of valueList()

valueList() is a bit of a rubbish function in its implementation as it is required to take a column reference, in the form query.columnName. This is the only place in CFML that this is the case, so - from the outset - alarm bells should have gone off with the implementors that this isn't a great approach. Furthermore, that is the only way of referencing a column that is permissible in valueList(). One cannot use query["columnName"] (which is also a reference to the entire column), nor any other sort of runtime expression. This causes problems if one is not in a position to hard-code the column name like that, for example if the column names are unknown at code-time, or indeed if even the query name is unknown at runtime. The latter is less often the case, granted. I have come across it though.

Firstly, the dynamic column name:

evaluate("valueList(query.#columnName#)");


That's what one has to do if one is using valueList(). But one does not need to use valueList() to effect this:

arrayToList(query[columnName]);

If one wants to emulate quotedValueList(); use listQualify() after arrayToList().

Secondly... what if the query name is a dynamic concern. One might think one can employ the previous tactic, of referencing the dynamically-named query thus:

valueList(variables[dynamicName].column);

This fails with:

Complex constructs are not supported with function valueList.

Use simple variable instead.

Which is a bit crap: it simply shouldn't matter how an expression is implemented, provided it is syntactically correct and returns the correct data type for the function taking it as an argument, it should work. What's particularly crap here is that this is a parser error (so done at compile time), not a runtime one, so CF has a built-in specific rule for valueList() to fail with expressions of this syntax. How about the compiler just lets the completely valid code through, and see if the value actually represents the desired data? Anyway,  I have digressed a bit.

Traditionally the fallback here is to do this:

evaluate("valueList(#dynamicName#.column)");

This is not necessary, because one can avoid using evaluate() by simply using a known variable:

knownVariable = variables[dynamicName];
valueList(knownVariable.column));

Still no need for evaluate().

Dynamically referencing scopes

This is a less common one, but I have seen this in the past. Sometimes the runtime part of a variable reference might be the scope, rather than the variable within the scope. I've seen this being done in the past (ages ago), but can't recall the situation. However here's a quick contrived example... a lightweight scope cache:

component {

    function init(scope){
        variables.scope = arguments.scope;
        "#variables.scope#.scopeCache" = {};

        return this;
    }

    function put(key, value){
        "#variables.scope#.scopeCache.#key#" = value;
    }

    function get(key){
        return evaluate("#variables.scope#.scopeCache.#key#");
    }

}

This could be used like this:

cache = new Cache("application");
cache.put(key="foo", value="bar"); // now cached in application scope
moo = cache.get(key="foo");

Output:

struct
applicationnamescopeCache022
scopecache
struct
foobar

So within the cache, we can create the variables without resorting to evaluate(), but fishing them back out again, we resort to using evaluate(). For example we can't do this:

theScope = "application";
theVariable = "foo";
moo = [theScope][theVariable];

But we can kinda do that. We can create a reference to scope easily enough, and then we can use a dynamic reference to that reference. Here's the non-evaluate() version of that code:

component {

    function init(scope){
        variables.scopeReferences = {
            request = request,
            session    = session,
            application = application
        };

        variables.scope = arguments.scope;

        variables.scopeReferences[variables.scope].scopeCache = {};

        return this;
    }

    function put(key, value){
        variables.scopeReferences[variables.scope].scopeCache[key] = value;
    }

    function get(key){
        return variables.scopeReferences[variables.scope].scopeCache[key];
    }

}

Here we set up some variables-scoped references to actual scopes (yeah, it breaks encapsulation... this is just example code), which means we can then use associative-array notation to reference the scopes dynamically at runtime.

Dynamically referencing object methods

Another situation in which one might think to use evaluate() in the past was to call a dynamically-named method on a CFC object, eg:

theMethodName = "theMethod";
theAnswer = someObject[theMethodName](someArg="someValue");


Railo supports that syntax, and bloody ColdFusion should too (people have lobbied for it, deaf ears have ensued from Adobe, thusfar). We've always had a tag-based way of effecting this, in <cfinvoke>:

<cfinvoke component="#someObject#" method="#theMethodName#" somearg="someValue" returnvariable="theAnswer">

However a fat lot of use that is in a CFScript block, which is where one's object-method-calling code generally ought to be.

ColdFusion 10 added a completely inelegant offering in lieu of the bracket-based syntax work as it should, in invoke():

theAnswer = invoke(theObject, theMethodName, {someArg="someValue"});

However prior to that, one might use evaluate():

theAnswer = evaluate('someObject.#theMethodName#(someArg="someValue")');

One can still get away without using evaluate() here, with a bit of messing around:

theObject.___someStaticMethodName = theObject[theMethodName]; // the ___ is just cos we want something unlikely to overwrite another method already in the object
theAnswer = theObject.___someStaticMethodName(someArg="someValue");

That's a bit more code that with evaluate(), but I still think it's a bit more aesthetically pleasing that parsing & executing CFML at runtime... because bear in mind that at runtime, the natural order of things is that we're running Java bytecode, not CFML. So it goes a bit against the grain to be running CFML as well. Still: that's mostly just aesthetics.

Performance

I think the reason most people acting avoid using evaluate() is because it runs so damned slowly. But... um... here's the thing. It doesn't. It runs marginally slower than most of the non-evaluate() approaches (but not all of them!), but we're talking on a scale of 10ths / 100ths of milliseconds difference per operation. IE: on my machine 10000 iterations of my test rig for comparing dynamic variable evaluation compared to using associative array syntax, the difference was about 100ms. so 0.01ms (10 microseconds) per iteration difference. Another way of looking at it - if you really like wringing your hands over performance - is the evaluate() version is twice as slow the associative array notation, but at the scale we're talking about it simply doesn't matter.

Most of the operations I tested were about the same sort of comparison, except surprisingly the approach of injecting a statically-named reference to a method representing a dynamically named one was actually "significantly" slower than using evaluate(). I have no idea why, but there you go.

Testing Performance

My test rig for this was to do the usual approach of "loop over things a lot to 'amplify' the time an operation takes" whilst performing the test operation. I used 10000 iterations.

As well as that looping, I also used JMeter to run that code using five test threads each running five separate users to run the test code. So each test round was 25 sets of 10000 iterations, running as simultaneously as JMeter, my computer, and my CF install would allow. I was not timing the requests themselves, I was only timing the code within the requests, so there was no warm-up or tear-down overhead in my measurements.

I did two comparison tests: one round using one of the evaluate() techniques listed above, and the other using the non-evaluate() technique.

I can't stress enough that if - to test the performance of some code - you need to do this "loop over it heaps to see how long a lot of iterations take" approach, because the performance differences between the control and the test code is neglible when only doing a single operation, then just stop testing. Save yerself some time, cos the answer is: "it doesn't matter". Don't contrive an artificial situation in which you can see a difference. If you can't see the difference in a normal situation, then there is no difference worth worrying about. Doing an actual load test is different, because code can behave differently when it's run more than once at a time. Load testing for performance is essential.

Code

Here's the code I used.

Dynamic variable names

Firstly using evaluate():

tally=0;
start=getTickCount();
for (i=1; i <= URL.iterations; i++){
    tally +=evaluate("application.variable#i#");
}
writeLog(file="#application.applicationname#_evaluate", text="Elapsed: #getTickCount()-start#ms");

Then using associative array notation:

tally=0;
start=getTickCount();
for (i=1; i <= URL.iterations; i++){
    tally += application["variable#i#"];
}
writeLog(file="#application.applicationname#_array", text="Elapsed: #getTickCount()-start#ms");


There's no surprises here.

valueList() with dynamic values

Using evaluate():

coloursInMaori      = makeQuery("colour", ["whero","karaka","kowhai","kakariki","kikorangi","tawatawa","mawhero"]);
coloursInEnglish    = makeQuery("colour", ["red","orange","yellow","green","blue","indigo","violet"]);

start=getTickCount();
for (i=1; i <= URL.iterations; i++){
    languageToUse = "English";
    rainbow = evaluate("valueList(coloursIn#languageToUse#.colour)");

    dynamicColumn = "id";
    englishColumnData = evaluate("valueList(coloursInEnglish.#dynamicColumn#)");
}
writeLog(file="#application.applicationname#_queryEvaluate", text="Elapsed: #getTickCount()-start#ms");

And avoiding it:
coloursInMaori      = makeQuery("colour", ["whero","karaka","kowhai","kakariki","kikorangi","tawatawa","mawhero"]);
coloursInEnglish    = makeQuery("colour", ["red","orange","yellow","green","blue","indigo","violet"]);

start=getTickCount();
for (i=1; i <= URL.iterations; i++){
    languageToUse = "English";
    queryToUse = variables["coloursIn#languageToUse#"];
    rainbow = valueList(queryToUse.colour);

    dynamicColumn = "id";
    englishColumnData = arrayToList(coloursInEnglish[dynamicColumn]);
}
writeLog(file="#application.applicationname#_queryReference", text="Elapsed: #getTickCount()-start#ms");

NB: makeQuery() is just a UDF which makes a query with cols ID and [as specified in the function call], with a row for each element in the array.

Dynamicially referencing scopes

This lot is a bit more complicated. We've got an Application.cfc which sets some things into the application scope (so as to not need to do so each test), a coupla implementations of that scope cache thing I mentioned above, and the two test scripts.

// Application.cfc
component {

    this.name                = "scopeCache021";
    this.sessionManagement    = true;

    function onRequestStart(){
        if (!structKeyExists(application, "scopeCaches")){
            application.scopeCaches = {
                evaluate    = new ScopeCacheEvaluate("application"),
                reference    = new ScopeCacheReference("application")
            };
        }
    }
}

I need to put that code in onRequestStart() because the reference-based cache CFC (see below) refers to the session scope, which doesn't exist yet in onApplicationStart().

// ScopeCacheEvaluate.cfc
component {

    function init(scope){
        variables.scope = arguments.scope;
        "#variables.scope#.scopeCache" = {};
        
        return this;
    }
    
    function put(key, value){
        "#variables.scope#.scopeCache.#key#" = value;
    }
    
    function get(key){
        return evaluate("#variables.scope#.scopeCache.#key#");
    }

}

// ScopeCacheReference.cfc
component {

    function init(scope){
        variables.scopeReferences = {
            request = request,
            session    = session,
            application = application
        };
        
        variables.scope = arguments.scope;

        variables.scopeReferences[variables.scope].scopeCache = {};
        
        return this;
    }
    
    function put(key, value){
        variables.scopeReferences[variables.scope].scopeCache[key] = value;
    }
    
    function get(key){
        return variables.scopeReferences[variables.scope].scopeCache[key];
    }

}

// usingEvaluate.cfm
for (i=1; i <= URL.iterations; i++){
    application.scopeCaches.evaluate.put(key="variables#i#", value="value #i#");
}
start=getTickCount();
for (i=1; i <= URL.iterations; i++){
    application.scopeCaches.evaluate.get(key="variables#i#");
}
writeLog(file="#application.applicationname#_evaluateCache", text="Elapsed: #getTickCount()-start#ms");

// usingScopeReferences.cfm
for (i=1; i <= URL.iterations; i++){
    application.scopeCaches.reference.put(key="variables#i#", value="value #i#");
}
start=getTickCount();
for (i=1; i <= URL.iterations; i++){
    application.scopeCaches.reference.get(key="variables#i#");
}
writeLog(file="#application.applicationname#_referenceCache", text="Elapsed: #getTickCount()-start#ms");

Object method tests

This time we have a Numbers.cfc which we are calling methods from, plus three tests: using evaluate(), using method reference injection, as well as a "control" which just uses <cfinvoke>.

// Numbers.cfc
component {

    variables.maori        = ["tahi", "rua", "toru", "wha"];
    variables.english    = ["one", "two", "three", "four"];

    public function inMaori(number){
        return variables.maori[number];
    }

    public function inEnglish(number){
        return variables.english[number];
    }

}

<!--- usingEvaluate.cfm --->
<cfparam name="URL.iterations" type="integer">
<cfset start=getTickCount()>
<cfloop index="i" from="1" to="#URL.iterations#">
    <cfset languageToUse = "Maori">
    <cfset evaluate("application.numbers.in#languageToUse#(number=1)")>

    <cfset languageToUse = "English">
    <cfset evaluate("application.numbers.in#languageToUse#(number=2)")>
</cfloop>
<cflog file="#application.applicationname#_evaluate" text="Elapsed: #getTickCount()-start#ms">

<!--- usingReferenceInjection.cfm --->
<cfparam name="URL.iterations" type="integer">
<cfset start=getTickCount()>
<cfloop index="i" from="1" to="#URL.iterations#">
    <cfset languageToUse = "Maori">
    <cfset application.numbers.__staticallyNamedMethod = application.numbers["in#languageToUse#"]>
    <cfset application.numbers.__staticallyNamedMethod(number=1)>

    <cfset languageToUse = "English">
    <cfset application.numbers.__staticallyNamedMethod = application.numbers["in#languageToUse#"]>
    <cfset application.numbers.__staticallyNamedMethod(number=2)>
</cfloop>
<cflog file="#application.applicationname#_reference" text="Elapsed: #getTickCount()-start#ms">

<!--- usingCfinvoke.cfm--->
<cfparam name="URL.iterations" type="integer">
<cfset start=getTickCount()>
<cfloop index="i" from="1" to="#URL.iterations#">
    <cfset languageToUse = "Maori">
    <cfinvoke component="#application.numbers#" method="in#languageToUse#" number="1">

    <cfset languageToUse = "English">
    <cfinvoke component="#application.numbers#" method="in#languageToUse#" number="2">
</cfloop>
<cflog file="#application.applicationname#_cfinvoke" text="Elapsed: #getTickCount()-start#ms">

I should do another test using invoke(), I guess, just to see how that fares.


That's it for the code. That's really just there in case anyone spots where my tests are unbalanced. I've tried to make each test analogous to the other test(s) it's being measured against.

Summary of results

Here's a sample test run's logged output:

"Severity","ThreadID","Date","Time","Application","Message"
"Information","catalina-exec-8","08/08/13","20:26:45",,"C:\apps\adobe\ColdFusion\10\cfusion\logs\evaluatePerformance010_array.log initialized"
"Information","catalina-exec-8","08/08/13","20:26:45","EVALUATEPERFORMANCE010","Elapsed: 98ms"
"Information","catalina-exec-13","08/08/13","20:26:45","EVALUATEPERFORMANCE010","Elapsed: 41ms"
[and continued in the same vein for 75 records]


And a summary of each of the tests...

Dynamic variable names



Stat\Testevaluate()Array
Min3221
25%3422
Mean3622
75%3923
Max19998

Here the Max values are definite outliers, as everything else is grouped around 30-odd or 20-odd milliseconds. On the face of it that's a dramatic "improvement" using associative array notation, but bear in mind that's over 10000 iterations. So an insignificant "real world" difference. If one is processing 10000 iterations of something on a regular basis, there are going to be more relevant things to be investigating than this difference.

valueList()

This test both dynamic query names and dynamic columns name.


Stat\Testevaluate()Array
Min6545
25%6847
Mean7048
75%72.550
Max10958

This is about the same difference. The values are higher across the board, I guess, because there were two tests being performed in this test rig, rather than the single one previously.

Dynamic scope referencing

These are the tests using those pared down "scope cached". We're using CFC method calls here, so that seems to blow out performance in general.

Stat\Testevaluate()Reference
Min12077
25%13988
Mean176107
75%280175.5
Max536438

But it's still a similar sort of performance comparison. Still bear in mind that whilst 176ms vs 107ms sounds like a lot compared to the other results so far, bear in mind that that equates to 0.0069ms per iteration difference. So: nothing.

Dynamic object methods

This time around we have three columns: evaluate(), inserting object references, and using <cfinvoke>.

Update:

I've redone the test and the results table to include invoke().
Stat\Testevaluate()Reference<cfinvoke>invoke()
Min1211719383
25%141181.2510290
Mean16024210898
75%232.5334.25126.25132
Max475731245216

This is the interesting result: using method-reference injection is slower than using evaluate(). I was surprised by this, so ran the tests repeatedly to see if there was a difference, but the results were generally the same. I'm not on the same machine currently as I did those tests, so I cannot add another column for invoke() as well, but I'll do that this evening (update: done... invoke is fractionally faster than <cfinvoke>, it seems. But inconsequentially so). Still: I don't think the difference is worth worrying about, but it's interesting that creating a function reference and inserting it into an object has any sort of overhead at all. And <cfinvoke> is most performant, but not really so much ahead of evaluate() in this case. I guess the overhead of calling methods on objects overwhelms the difference between doing if with/without evaluate().

Conclusion

I think using evaluate() is always a poor choice syntactically, because there's almost always an approach that uses more elegant code that has the benefit of being compiled before it's run. And I think even the more verbose "work-arounds" like with the scope cache or injecting methods into an object are tidier code.

But one consideration I will not be dwelling on from now on is that I will not give any thought to performance considerations. Because they just aren't there. The performance consideration did used to be there on older versions of ColdFusion. I wonder how long it's not been the case though? Certainly by ColdFusion 9, which I used to test at work yesterday, before gathering my more comprehensive results on CF10 last night. I've yet to try Railo.

You?

Have you got any other use cases in which you're using evaluate(), or have seen it in use? Or do you have your own opinions on this? Sing out!

Cheers.

--
Adam

Thursday, 8 August 2013

Tuesday, 6 August 2013

ColdFusion & JSON: yet another bug

G'day:
This might actually be a manifestation of the same issue I wrote about a while back: "Right... so JSON is being a pain in the arse again". And I didn't spot this one myself - I've given up on using JSON in ColdFusion as it's just too unstable - but read about it on a thread on CF-TALK (it's not in the archive yet, so I cannae link to it).

Anyway, this time the issue is with ColdFusion and "strings that look like numbers" again, but a different slant on it. Consider this code:

number = .0006;
struct = {number=number};
json = serializeJSON(struct);
writeDump([
    {number=number},
    {"struct.number"=struct.number},
    {struct=struct},
    {json=json}
]);

On ColdFusion (9, and apparently 10 too, going from the thread I can't link you to...), the output is this:

array
1
struct
NUMBER.0006
2
struct
struct.number.0006
3
struct
STRUCT
struct
NUMBER.0006
4
struct
JSON{"NUMBER":6.0E-4}

Nice. Thanks for that, ColdFusion. Just in case yer really going "wah??" there, that's scientific notation for 0.0006. So, ColdFusion... which part of "turn this into JSON" did you interpret as "turn this into JSON and any numbers with more then three decimal place? Scientific notation please". Just to demonstrate that this is not "just one of those things", here's the output in Railo:

Monday, 5 August 2013

Saturday, 3 August 2013

Saving class files in ColdFusion (and Railo): anecdote should not take precedence over analysis

G'day:
There was a thread the other day on the Railo Google Group regarding the "Save Class Files" setting on Railo. I piped up with my usual spiel that I don't think it's a worthwhile setting to use on ColdFusion:
Yeah, I can't vouch for Railo, but on ColdFusion saving class files rapidly becomes a performance hit on the system (Windows) if your app is such that you generate more than a thousand or so classes. The reason being - it seems - that Windows really struggles to file-scan a directory to find the pre-compiled class if it's needed... say the one in memory has been garbage collected... once there are more than a few hundred files in that directory. On big apps it's more expedient to let the class recompile from source code than rely on Windows finding and loading a saved one.

I doubt this is a problem on *nix-based file systems.

The issue with CF is that all the classes are stored in one flat directory. If they were stored hierarchically (which shouldn't be too much of an issue?), then there'd be no problem. I did mention this to the Adobe bods, but they looked at me like I was speaking [some language they didn't understand... CF perhaps], and that was that.

Anyway... I dunno if the same consideration exists on Railo, but this would be one reason why one might not want those class files saving. Something to test, perhaps.
Mark Drew subsequently suggested it might be a topic for a blog article, so here one is.

Friday, 2 August 2013

Blue... Phoenix?

G'day:
I saw mention of this on Twitter the other day and thought it was a typo. Then I saw Chris's blog post just now. Blue Dragon -  not OpenBD, but the New Atlanta product - still exists? And is in development?