In the ColdFusion 2016 release, the ability to create unsynchronised arrays was added. Adobe’s ColdFusion 2016 Performance Whitepaper claims a significant speed increase when you use them.
I thought I’d have a dig into this.
First up, how do you create them?
You create an unsynchronised array with arraynew
and a value of false
for the 2nd argument (the default is true). So for a one-dimensional unsynchronised array you’d write this:
a = arrayNew(1, false);
I think the syntax is a bit clunky and we’d largely left arrayNew
behind but hey-ho
I knocked up the following to have a look at the datatype:
function objectType(o) { return getMetaData(o).getCanonicalName(); } unsynchronisedArray = arrayNew(1, false); writeDump("unsynchronisedArray => " & objectType(unsynchronisedArray)); synchronisedArray = arrayNew(1, true); writeDump("synchronisedArray => " & objectType(synchronisedArray)); legacyArray = arrayNew(1); writeDump("legacyArray => " & objectType(legacyArray)); modernArray = []; writeDump("modernArray => " & objectType(modernArray)); clonedArray = unsynchronisedArray[:]; writeDump("clonedArray => " & objectType(clonedArray));
Running the above on ColdFusion 2018 Update 2 we get:
unsynchronisedArray => coldfusion.runtime.FastArray synchronisedArray => coldfusion.runtime.Array legacyArray => coldfusion.runtime.Array modernArray => coldfusion.runtime.Array clonedArray => coldfusion.runtime.FastArray
From this we can see that there is a new datatype of coldfusion.runtime.FastArray
and that the previous ways to create an array all return a coldfusion.runtime.Array
– which although I’ve never stopped to think about it – means that we’ve been using synchronised arrays all along.
I was pleased to see that cloning the unsynchronisedArray
preserves the datatype.
Now that we have a FastArray – what use is it?
My first thought was does it get passed around as an ‘traditional’ array does. In ColdFusion, by default, arrays are passed my value, not by reference (you can override this behaviour in you application by using this.passArrayByReference
)
function mutateArray(array a) { a.append(2); return a; } before = arrayNew(1, false); before.append(1); after = mutateArray(before); writeDump(before); writeDump(after);
Sure enough, the before and after arrays are different, so the array is being passed by value, so the behaviour is the same as we’ve been used to with arrays.
Next up, I thought I’d go for a simple speed test.
iterations = 100000; function doSomething(boolean isSynchronized) { var result = arrayNew(1, isSynchronized); var i = 0; while (i++ < iterations) { result.append(i); } return result; } function testAppendSpeed(type) { var s = getTickCount(); var result = doSomething(type); var e = getTickCount(); return { "first": result[1], "last": result[iterations], "ms": e-s }; } writeDump(testAppendSpeed(true)); writeDump(testAppendSpeed(false));
I ran this a few times on 2016 Update 8 and here’s the overall average benchmark in milliseconds.
Synchronised: 83.9ms Unsynchronised: 75.3ms
I then ran it the same number of times on 2018 Update 2 and here’s the overall average benchmark in milliseconds.
Synchronised: 82.0ms Unsynchronised: 88.3ms
From that it looks like the Unsynchronised array is faster in ColdFusion 2016 Update 8, but slower on ColdFusion 2018 Update 2. Here’s the code on cffiddle:
Let’s give sorting a go to see how that compares.
iterations = 100000; function buildArray(boolean isSynchronized) { var result = arrayNew(1, isSynchronized); var i = 0; while (i++ < iterations) { result.append(i); } return result; } function testSortSpeed(type) { var array = buildArray(type); var s = getTickCount(); arraySort(array, "numeric", "desc"); var e = getTickCount(); return { "first": array[1], "last": array[iterations], "ms": e-s }; } writeDump(label="synchronized", var=testSortSpeed(true)); writeDump(label="unsynchronized", var=testSortSpeed(false));
I ran this a few times on 2016 Update 8 and here’s the overall average benchmark in milliseconds.
Synchronised: 4.2ms Unsynchronised: 5.7ms
I then ran it the same number of times on 2018 Update 2 and here’s the overall average benchmark in milliseconds.
Synchronised: 10.5ms Unsynchronised: 9.5ms
From those results is seems that the Synchronised array is actually marginally faster on 2016 and slower on 2018.
So what is the difference between the two types of arrays?
I had a look at the class hierarchy (on 2018 Update 2) to see if they were different Java datatypes. Here’s the unsynchronised ‘fast’ array:
SELF coldfusion.runtime.FastArray PARENT java.util.ArrayList GRANDPARENT java.util.AbstractList GREATGRANDPARENT java.util.AbstractCollection
and the unsynchronised ‘traditional’ array hierarchy is:
SELF coldfusion.runtime.Array PARENT coldfusion.runtime.FastArray GRANDPARENT java.util.ArrayList GREATGRANDPARENT java.util.AbstractList
So that was interesting, but doesn’t really give up any clues as there is no difference at the Java level.
So, we’ll have to go with what the docs say:
In ColdFusion 11, and previous versions, arrays were always thread-safe.
But thread safety comes at a cost of synchronizing the array object. In a synchronized array, two or more threads cannot access the array at the same time. The second thread has to wait until the first thread completes its job, resulting in significant performance deterioration.
In ColdFusion (2016 release), you can use an unsynchronized array and let multiple threads access the same array object simultaneously.
You can use unsynchronized arrays in situations where you need not worry about multiple threads accessing the same array. In other words, if you are defining an unsynchronized array in a thread safe object, like a UDF or a CF-Closure, you can use an unsynchronized array.
While a normal array is slower but thread safe, an unsynchronized array is faster by more than 90%.
https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-functions/functions-a-b/arraynew.html
From that it sounds like we can use unsynchronised arrays anywhere that can’t be simultaneously accessed (race condition). So don’t use them in shared scopes or if you have a component stored in the application scope which mutates an array used for state.
The Bottom line
From my rudimentary tests, it’s unlikely that the speed of the ‘traditional’ arrays is going to be a performance bottle neck in your application in the first place. I’m creating arrays with 100,000 elements in my example in a 10th of second, so not exactly sluggish.
From the speed gains I’m seeing (and I may be completely missing how you should use them to get that 90% speed increase the docs refers to) I think it’s best to write code using synchronised (traditional) arrays until you have a performance issue with mutating arrays and then look at if you can use an unsynchronised (fast) array instead.
The post A look at unsynchronised arrays in CFML appeared first on ColdFusion.