Migration Solutions for ColdFusion Applications to ASP.NET
      
Vince Bonfanti's Weblog

BlueDragon.NET versus ColdFusion 8: .NET Integration Performance

CFML-to-.NET integration is the ability to create and invoke methods on .NET objects from CFML using the CFOBJECT tag and CreateObject function. In a previous blog entry I discussed the architectural differences between BlueDragon.NET and the implementation of the ColdFusion 8 (CF8) .NET Integration feature. To summarize:

  • CF8 implements .NET integration via remote procedure calls (RPC) to an external process and a Java-to-.NET bridge.
  • BlueDragon.NET is implemented as an in-process extension to ASP.NET; creating and invoking methods on .NET objects is done using the .NET reflection APIs.

The implications of these architectural differences for CFML-to-.NET integration are:

  • BlueDragon.NET performance is much better than CF8;
  • BlueDragon.NET provides much better compatibility with .NET than CF8 (CF8 imposes limitations that BlueDragon.NET does not); and,
  • BlueDragon.NET provides a wealth of opportunities for integration with IIS and ASP.NET that CF8 does not support at all.

In this blog entry I'd like to delve into more detail on the first point: performance.

To test performance, I found these two examples: Ben Forta's GetDriveInfo UDF and Anuj Gakhar's System.Environment example. I combined these into a single CFML page, wrapped them each in a CFTIMER tag, then enabled debug output to display the page execution times and CFTIMER output. Here's what the code looks like:

<cftimer label="GetDriveInfo" type="debug">

    <!--- Test with all drives --->
    <h3>All Drives</h3>
    <cfdump var="#GetDriveInfo()#">
    
    <!--- Test with just C: drive --->
    <h3>C: Drive</h3>
    <cfdump var="#GetDriveInfo("C")#">
    
    <!--- Display just space on C: drive --->
    <h3>Free Space On C Drive</h3>
    <cfdump var="#NumberFormat(GetDriveInfo("C").freespace)#">
    
    <p></p>
</cftimer>

<cftimer label="System.Environment" type="debug">
    <cftry>  
        <cfobject type=".net" class="System.Environment" name="env" action="connect">  
    <cfcatch type="any">  
        <cfobject type=".net" class="System.Environment" name="env" action="create">  
    </cfcatch>  
    </cftry>
    
    <cfset stargs = StructNew()>  
    <cfset stargs.cwd = env.Get_CurrentDirectory()>  
    <cfset stargs.machineName = env.Get_MachineName()>  
    <cfset stargs.platform = env.Get_OsVersion().Get_Platform().ToString()>  
    <cfset stargs.servicepack = env.Get_OsVersion().Get_ServicePack()>  
    <cfset stargs.versionString = env.Get_OsVersion().Get_VersionString()>  
    <cfset stargs.processorCount = env.Get_ProcessorCount()>  
    <cfset stargs.systemDirectory = env.Get_SystemDirectory()>  
    <cfset stargs.newline = env.Get_NewLine()>  
    <cfset stargs.logicalDrives = env.GetLogicalDrives()>  
    <cfset stargs.envVariables = env.GetEnvironmentVariables()>  
    <cfdump var = "#stargs#">
</cftimer> 


<!--- ================================================================== --->

<!--- Get drive details for one or all drives --->
<cffunction name="GetDriveInfo" returntype="query" output="false">
    <cfargument name="drive" required="no" default="">

    <!--- Local vars --->
    <cfset var result=QueryNew("name,type,isready,format,label,totalsize,freespace",
                     "varchar,varchar,bit,varchar,varchar,double,double")
>

    <cfset var sidiClass="">
    <cfset var drives="">
    <cfset var i=0>

    <!--- Get System.IO.DriveInfo class --->
    <cfobject type=".NET" name="sidiClass" class="System.IO.DriveInfo">
    <!--- Get drives --->
    <cfset drives=sidiClass.GetDrives()>

    <!--- Loop through drives --->
    <cfloop from="1" to="#ArrayLen(drives)#" index="i">
        <!--- Check if need this one --->
        <cfif ARGUMENTS.drive IS ""
            OR ARGUMENTS.drive EQ drives[i].Get_Name()
            OR (Len(ARGUMENTS.drive) IS 1
            AND ARGUMENTS.drive EQ Left(drives[i].Get_Name(), 1))>

            <!--- Add row --->
            <cfset QueryAddRow(result)>
            <!--- Get name, type, and ready flag --->
            <cfset QuerySetCell(result, "name", drives[i].Get_Name())>
            <cfset QuerySetCell(result, "type", drives[i].Get_DriveType().ToString())>
            <cfset QuerySetCell(result, "isready", drives[i].Get_IsReady())>
            <!--- Get extra details ONLY if ready, or will throw error --->
            <cfif drives[i].Get_IsReady()>
                <cfset QuerySetCell(result, "format", drives[i].Get_DriveFormat())>
                <cfset QuerySetCell(result, "label", drives[i].Get_VolumeLabel())>
                <cfset QuerySetCell(result, "totalsize", drives[i].Get_TotalSize())>
                <cfset QuerySetCell(result, "freespace", drives[i].Get_AvailableFreeSpace())>
            </cfif>
        </cfif>
    </cfloop>

    <!--- Return result --->
    <cfreturn result>
</cffunction>

On Windows Server 2003, I executed the example page 20 times each on CF8 and BlueDragon.NET, then took a snapshot of the timings produced by the 20th execution. Here are the timings produced by CF8 (click for a larger image):

Here are the timings produced by BlueDragon.NET (click for a larger image):

For the GetDriveInfo example, CF8 is about 3.5 times slower than BlueDragon.NET (219ms versus 62ms). For the System.Environment example, the results are even more dramatic: CF8 is more than 15 times slower than BlueDragon.NET (250ms versus 16ms). In overall page execution times, CF8 is about 6 times slower than BlueDragon.NET (469ms versus 78ms).

These results are not surprising, based on an understanding of the architectural differences between BlueDragon.NET and the implementation of the CF8 .NET Integration feature.

(In my previous blog entry I stated that the CF8 .NET Integration feature is architecturally very similar to web services calls. Given these poor timings, it might be an interesting exercise to redo these examples as .NET web services, and then see if the CF8 .NET Integration feature provides any performance benefit over web services at all).

If you're planning to use the CF8 .NET Integration feature, you'll have to be very concerned about performance. As these examples demonstrate, it's very easy to get execution times into the 200-250ms range just for creating and invoking methods on a single .NET object, and into the 400-500ms range for multiple objects per page. You might want to consider a caching strategy where the results of .NET method invocations are stored in a shared variable scope--such as Session or Application--rather than making .NET method calls for every request (of course, this won't always be possible). It's probably best to avoid creating and invoking methods on multiple .NET objects for a single request, at least on a regular basis.

With BlueDragon.NET, performance is much less of a concern. You'll generally be free to create and invoke methods on multiple .NET objects, on a per-request basis, without worrying about your page execution times extending up into the 400-500ms range (as they can with CF8).

In future blog entries I'll follow-up on advantages of .NET compatibility and ASP.NET integration provided by BlueDragon.NET.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Does this change if the .NET object is persisted in a scope? so like once they are up and running etc what's the performance like then?
# Posted By zac spitzer | 8/28/2008 5:19 AM
Zac, your question boils down to: "What is the relative cost of object creation versus method invocation?"

I modified the test page to do separate timings for object creation and method invocation for the System.Environment example. On both CF8 and BD.NET, the object creation time is consistently reported as 0ms--meaning it's too small to be measured--and all of the time was taken by the method invocations.

At least in this example, it would appear that persisting the objects in a shared scope--and thereby avoiding object creation--won't give you any benefit.
# Posted By vinceb | 8/28/2008 11:55 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.2.001. Contact Blog Owner

company media information terms of use privacy policy contact us
This page was dynamically built on the BlueDragon CFML Engine