Damon Cooper's BLOG
Viewing By Day : November 10, 2004 / Main
November 10, 2004
Behold, the Power of Threads
Here's a quick example:

Say you had a CFM page that had to do 100 pieces of work, each taking a while, that were needed to process the page, but they had little or no dependence on each other. Up till now, you'd have very few options other than to do each of the 100 items serially, and finish the page when all items are complete.

Wouldn't it be great if you could harness the multithreaded power of modern operating systems in your CFML code to split these work items up so they run in parallel, yet still be able to wait for them to all to finish (synch up) so the processing could complete as before and return to the user with the same results?

What kind of benefit would you expect to see?

In the best case, if none of the work items were related, say, you could spawn 100 threads (or better yet, if you could have a maintained 'pool' of ready waiting threads already spawned), and get these threads loaded up just with the work you wanted them to do, and the majority of the work could be done completely in parallel. In theory, you could achieve nearly 100X improvement, assuming there were no other 'work' related bottlenecks, etc. In other words, you could theoretically get all 100 pieces of work done in about the same time as ONE piece of work.

...and that would be sweet indeed.

In the example attached, the page with 100 pieces of work takes about 20,780ms (20 seconds) to complete.

In the Blackstone multithreaded example attached, the page accomplished the SAME work, but only takes about 230ms to complete.

This page also waits for ALL the work to get finished (like before), but because of the massive parallelism and scalability offered by using multiple threads, total user response time is improved by about 20,550 ms, or an improvement approaching 100X.

Very dramatic stuff, and your users will love you for it.

(SPECIAL NOTE: these results are not indicitive in any way of the performance of the final Blackstone product, and are provided only for discussion. Also note that Blackstone Beta users are under strict NDA, so please be careful if commenting on this post not to violate your NDA).

Imagine spawning off long running DB queries in parallel, for example. Page response times for most existing CF applications, in fact, would probably have some place that could make use of such a capability to dramatically make the user experience better.

Of course, anytime you can make these kinds of dramatic improvements to existing apps with little work, you'll look like a hero. And Blackstone is all about making you look like a hero to your users.

Below is a brief description of THE EXAMPLE FILES and how to set this up to run on your machine. This assumes you've got a Blackstone Beta installed. You'll also need to modify the CFFILE action='COPY' source='C:\Baseline.txt' destination='C:\____WORKERFOLDER\#x#.txt' line in 'Threadsynch_serial.cfm' and 'dowork.cfc' with a real source file to copy and destination folder to write 100 files to on YOUR machine.

1) 'Threadsynch_serial.cfm' does 100 pieces of long running 'work' in a CFLOOP, using CFFLUSH to keep the user informed, before completing. (The 'work' is comprised of copying a file and 'sleeping' for 200ms.)

(turn on debugging output in the Admin to see the total page execution times.)

2) 'Threadsynch.cfm' removes the 'work' out into a CFC, called 'dowork.cfc'. In the main work CFLOOP, rather than do the work inline, it instead calls 'sendGatewayMessage()' with the name of the Asynchronous CFML Gateway instance (I setup in the Admin to point at the 'dowork.cfc'), and the work ID # (loop counter) used by the 'dowork.cfc'.

The 'dowork.cfc' accepts the work ID # (loop counter from the CFM), does that piece of 'work' (copy a file and wait 200ms) and increments a Server scope variable inside an exclusive lock.

3) 'Threadsynch.cfm' then loops while checking the Server variable where 'dowork.cfc' reports progress, until all work is done. A thread sleep is included between checks in the loop to reduce CPU usage in the 'wait' loop. Each loop iteration reports the current amount of work done so far and CFFLUSH's that out to the browser.

4) Once all the 'work' is done, the 'Threadsynch.cfm' completes (not before).

5) Set the Event Gateway Threads setting to 100 (since I have 100 messages coming in nearly simultaneously and I want to maximize performance in this case) and restart CF to take affect.

TO RUN:

1) Run 'Threadsynch_serial.cfm'. Note the execution time.
2) Run 'Threadsynch.cfm'. Note the execution time.
3) Stare in amazement.

Behold, the power of threads.

Enjoy,

Damon