Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I just discovered today that there are essentially two ways to calculate field values in ArcObjects:

1. Using ESRI.ArcGIS.GeodatabaseUI.ICalculator:

ITable table = …;
ICursor updateCursor = table.Update(null, false);

ICalculator calculator = new Calculator()
{
    Cursor = updateCursor,
    Field = …,
    Expression = "…"
};
calculator.Calculate();

Marshal.FinalReleaseComObject(updateCursor);

2. Using ESRI.ArcGIS.DataManagementTools.CalculateField:

var calculateField = new CalculateField() 
{
    in_table = …,
    field = …,
    expression = "…",
    expression_type = "VB"
};

var gp = new Geoprocessor();
gp.Execute(calculateField, null);

While performance of the two approaches is similar with File GDB tables, I've found that it varies a lot for SDE tables: I've measured a processing speed of 21 milliseconds per row with the former approach (ICalculator), while geoprocessing requires only 10 milliseconds per row (<50 %).

Does anyone know, or have an educated guess, why ICalculator is slower?

  • With the ICalculator approach, it doesn't seem to matter whether the cursor used is a recycling one or not. But apparently, an update cursor is required. (I tried using a search cursor, too, but that resulted in warnings and errors — discoverable via ICalculatorCallback — immediately after calling Calculate.)

  • Also with the former approach, I tried reducing network bandwidth usage by specifying only the minimum required SubFields in the IQueryFilter used to retrieve the update cursor. Surprisingly, this doesn't improve runtime performance at all.

  • I tried all above methods outside any edit session or explicit transaction.

share|improve this question
    
Did you start editing the workspace using ICalculator? –  cag May 14 '12 at 20:01
    
@cag: No, I am operating outside of an edit session. –  stakx May 14 '12 at 20:20
    
Did you try it with a regular cursor (via ITable.Search instead of ITable.Update)? This Esri example uses the Search method. –  Kirk Kuykendall May 14 '12 at 20:58
    
@Kirk: Yes, I did attempt to use a search cursor, but these cause both warnings and errors immediately after calling Calculate. I discovered this by installing an ICalculatorCallback via the ICalculator.Callback property. –  stakx May 14 '12 at 21:01

2 Answers 2

My guess for the discrepancy is that the GP method requires less COM interop marshaling since it handles the cursor business internally.

See also "Improving .NET Application Performance and Scalability" Chapter 7: Improving Interop Performance. Particularly:

Design Chunky Interfaces to Avoid Round Trips

When you design code that is to be called through P/Invoke or COM interop, design interfaces that reduce the number of calls required to complete a logical unit of work. This guideline is particularly important for interfaces that handle calls to COM components located on a remote server, where the performance impact of using chatty interfaces is significant.

The following code fragment illustrates a chatty component interface that uses property getters and setters and requires the caller to cross the managed/unmanaged code boundary three times, performing data marshaling, security checks, and thread switches each time.

MyComponent.Firstname = "bob";
MyComponent.LastName = "smith";
MyComponent.SaveCustomer();

The following code fragment shows a chunky interface designed to perform the same tasks. The number of round trips between managed and unmanaged code is reduced to one, which significantly reduces the overhead required to complete the logical operation.

MyComponent.SaveCustomer( "bob", "smith");
share|improve this answer
    
You seem to be suggesting that the ICalculator.Calculate method is actually implemented as managed code. I can't quite believe this. AFAIK, only the various tool classes surrounding the ESRI.ArcGIS.Geoprocessor assembly and the ADF classes are managed code. –  stakx May 14 '12 at 20:52
    
Not at all, I'm suggesting that since you create a cursor in managed code that this incurs more interop marshaling than if you let the GP do it. –  blah238 May 14 '12 at 21:00
    
Interesting thought. If I'm not mistaken, even if I create the cursor from managed code, there is still an unmanaged cursor object, which the unmanaged ArcGIS core can use directly. Interop overhead (marshalling, going through the Runtime Callable Wrapper, etc.) should only occur to the managed code portion here... right? –  stakx May 14 '12 at 21:09
    
I'm not really sure either; I think it depends on the implementation whether it has to marshal just once or for every row. I think in the case of ICalculator, because it has a callback, it does it for every row. But @SeaJunk's answer may be the real reason. Have you tried it with a standalone table instead of a feature class? –  blah238 May 14 '12 at 22:49
    
@stakx If you're passing a callback, then wouldn't that cause more interop traffic? (The callback class is a managed class after all). Did you try it without a callback?. –  Kirk Kuykendall May 15 '12 at 13:08

If its a feature table then ICalculator.Calculate is busy working out what the returned envelope will be after each row op even if you don't do anything with it. GP doesnt return anything so you will be slower with Icalc over a wire to SDE with direct connect.

share|improve this answer
    
Interesting. I cannot verify this right now, but envelope / index updating seems like a plausible explanation. –  stakx May 15 '12 at 19:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.