Performance of ArcObjects


In this topic


About performance of ArcObjects

ArcObjects is based on the Component Object Model (COM). For .NET objects to interoperate with COM objects, an intermediate layer is needed. The .NET Framework contains the Interop application programming interface (API), which acts as the intermediate layer between .NET and COM. The Interop API provides Runtime Callable Wrappers (RCWs) as the mechanism that allows the use of COM objects in .NET.
 
As shown in the following code example, define a COM object in .NET:
 

[C#]
ESRI.ArcGIS.Geometry.Point pt = new ESRI.ArcGIS.Geometry.Point();

[VB.NET]
Dim pt As ESRI.ArcGIS.Geometry.Point = New ESRI.ArcGIS.Geometry.Point()
An RCW object is defined instead of the COM object itself. When the object is instantiated (using the new keyword), the RCW object creates the appropriate COM object. When you call the members of the object in .NET, the parameters of the method call will be marshaled from the .NET process to the COM process, and the return value will be marshaled back.

Example 1: Looping ArcObjects geometry operations

To analyze the performance of ArcObjects in .NET, you need to consider the overhead of creating wrapper objects and marshaling, as compared to the actual running time of your ArcObjects code. The following code example shows this comparison.
This example code is not intended as a benchmark of ArcObjects performance in .NET but demonstrates possible differences in the speed of execution of ArcObjects code, which you may want to consider when creating applications. The example code was run on a machine with a processor speed of 3 GHz, with NET Framework version 2.0, using Visual Studio .NET 2005.
First, create a Windows application in C# or VB.NET, add a form with one button, and perform some simple geometry operations in a loop when the button is clicked.

[C#]
private void button1_Click(object sender, System.EventArgs e)
{
  // Prepare the objects.
  ESRI.ArcGIS.Geometry.IPoint p1, p2;
  p1 = new ESRI.ArcGIS.Geometry.Point();
  p2 = new ESRI.ArcGIS.Geometry.Point();
  p1.PutCoords(1, 2);
  p2.PutCoords(3, 4);
  ESRI.ArcGIS.Geometry.ILine pLine = new ESRI.ArcGIS.Geometry.Line();

  // Time a loop of geometry operations.
  int tick1, tick2, tick3;
  tick1 = System.Environment.TickCount;
  for(long i = 0; i <= 10000000; i++)
  {
    pLine.PutCoords(p1, p2);
  }
  tick2 = System.Environment.TickCount;
  tick3 = tick2 - tick1;
  
  System.Windows.Forms.MessageBox.Show(tick3.ToString());
}

[VB.NET]
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  ' Prepare the objects.
  Dim p1, p2 As ESRI.ArcGIS.Geometry.IPoint
  p1 = New ESRI.ArcGIS.Geometry.Point
  p2 = New ESRI.ArcGIS.Geometry.Point
  p1.PutCoords(1, 2)
  p2.PutCoords(3, 4)
  Dim pLine As ESRI.ArcGIS.Geometry.ILine = New ESRI.ArcGIS.Geometry.Line
  
  ' Time a loop of geometry operations.
  Dim tick1, tick2, tick3 As Integer
  tick1 = System.Environment.TickCount
  Dim i As Integer
  For i = 0 To 10000000
    pLine.PutCoords(p1, p2)
  Next i
  tick2 = System.Environment.TickCount
  tick3 = tick2 - tick1
  
  System.Windows.Forms.MessageBox.Show(tick3.ToString())
End Sub
This code took an average of approximately 30 seconds to run in C# and VB.NET. For comparison, use Visual C++ to create a standard executable (EXE), using the following code example. (The GetTickCount Windows API call replaces the .NET Framework TickCount property.)
 
[VC++]
LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
   IPointPtr ipP1(CLSID_Point);
   IPointPtr ipP2(CLSID_Point);
   ipP1->PutCoords(1.0,2.0);
   ipP2->PutCoords(3.0,4.0);
   ILinePtr ipLine(CLSID_Line);
   DWORD tick1, tick2, tick3;
   tick1 = ::GetTickCount();
 
   for(long i=0L; i<10000000L; i++)
   {
      ipLine->PutCoords(ipP1, ipP2);
   }
   tick2 = ::GetTickCount();
   tick3 = tick2 - tick1;
  
   TCHAR msg[255];
   _stprintf(msg, _T("Total Time: %ld ms"), tick3);
   ::MessageBox(0, msg, _T(""), MB_OK);
  
   return 0;
}
 
This equivalent Visual C++ code takes approximately 5 seconds to execute, making the code about six times faster than .NET. However, the previous example is an extreme case, performing a small operation thousands of times that executes very quickly. In cases like this, the time spent in the interoperation between COM and .NET is likely to dominate the total running time.
 

Example 2: Creating and populating a geodatabase

Consider instead example code that creates and populates a personal geodatabase. A personal geodatabase is first created, then populated with features by importing the shapefiles that comprise the sample data. Subtypes are created, and a geometric network with connectivity rules is built. A composite relationship class is also created. In contrast to Example 1, this code consists of operations that take longer to execute.
 
In C# and VB.NET, this code takes an average of 25 seconds to run, and in VB6, the execution time also averages approximately 25 seconds. How can the .NET code execute just as quickly, considering the extra instructions required for the interop layer? The VB6 code is executed by the Visual Basic runtime, which can be less efficient than the .NET Framework. For this example then, no difference is seen in performance between the environments.
 

Example 1 revisited: Creating a proxy class in Managed C++

Realistically, you may need to perform the kind of operation that took so much longer in .NET—large iterations of small operations. Do you have to take the performance hit?
 
Not necessarily. You have the choice to write a proxy class in Managed C++ where you can place ArcObjects code that uses the interop layer heavily. Once you expose this code as public methods, you can call the methods from your C# or VB.NET code. Since the interop layer is only required when you call the methods of this proxy class, the execution time can be greatly reduced.
 
For example, to perform the previous operations, you can create a proxy class in Managed C++. This class will contain a function that performs the loop of geometry operations.
 
[C++]
 
#using "C:\Program Files\ArcGIS\Dotnet\ESRI.ArcGIS.System.dll"
#using "C:\Program Files\ArcGIS\Dotnet\ESRI.ArcGIS.Geometry.dll"
  
namespace MCpp
{
  public __gc class AOWrapper
  {
    public:
      AOWrapper()
    {
      ::CoInitialize(0);
    }
    ~AOWrapper()
    {
      ::CoUninitialize();
    }
    public:
      int line_test()
    {
      IPointPtr ipPt1(CLSID_Point);
      IPointPtripPt2(CLSID_Point);
      ipPt1->PutCoords(1, 2);
      ipPt2->PutCoords(2, 3);
      ILinePtr ipLine(CLSID_Line);
      for(long i = 0;  i < =  10000000; i++)
      {
        ipLine->PutCoords(ipPt1,ipPt2);
      }
      return 0;
    }
  };
}
 
This function can be called from a .NET application using code like the following (remember to add a reference to the .dll created previously):
 

[C#]
private void button1_Click(object sender, System.EventArgs e)
{
  int tick1, tick2, tick3;
  MCpp.AOWrapper w = new MCpp.AOWrapper();
  tick1 = System.Environment.TickCount;
  w.line_test();
  tick2 = System.Environment.TickCount;
  tick3 = tick2 - tick1;
  System.Windows.Forms.MessageBox.Show(tick3.ToString());
}

[VB.NET]
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  Dim tick1, tick2, tick3 As Integer
  Dim w As MCpp.AOWrapper = New MCpp.AOWrapper()
  tick1 = System.Environment.TickCount
  For i = 0 To 10000000
    pLine.PutCoords(p1, p2)
  Next i
  tick2 = System.Environment.TickCount
  tick3 = tick2 - tick1
  
  System.Windows.Forms.MessageBox.Show(tick3.ToString())
End Sub
Using the proxy class written in Managed C++, the average running time is improved to approximately 5 seconds.
 
One disadvantage of using Managed C++ is that the assembly written in Managed C++ cannot use the verification mechanism of .NET security, so it needs to run in a trusted environment. Future releases of Visual C++ .NET may add additional functionality that may alter this situation.
 
The key to improving performance is to find the performance bottleneck of your application, which may or may not be the interop time. For most ArcObjects applications, developing in .NET will not significantly deteriorate the performance of your code. If you find the interop is indeed the bottleneck, writing Managed C++ proxy classes for parts of your code could provide a solution.


See Also:

Managed Extensions for C++ Programming
Dr. GUI on Visual C++ .NET