关于 ArcObjects 的性能

来源 目录

关于 ArcObjects 的性能

我们知道,ArcObjects 是基于 COM (组件对象模型)技术之上。 为了使 .NET 对象与 COM 对象实现互操作,需要一个中间层。 .NET Framework 包含的【互操作 API】,它充当了 .NET 和 COM 之间的中间层。 互操作 API 提供 RCW(运行时可调用包装器) 作为 【允许在 .NET 中使用 COM 对象的机制】。

以下代码示例显示了如何在 .NET 中定义 COM 对象:

[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()

一个RCW 对象被定义了,而不是 COM 对象。 当实例化一个 COM 对象(使用 new 关键字)时,RCW 对象会创建对应的 COM 对象。 当你在.NET中调用 COM 对象的成员时,方法调用的参数会从 .NET 进程被封送到COM进程,返回值会被封送回来。

示例 1

循环 ArcObjects 的 Geometry 操作

要分析 .NET 中 ArcObjects 的性能,请考虑创建包装对象(RCW)和编组(Marshalling)的开销,与 ArcObjects 代码的实际运行时间相比。 以下代码示例显示了这种比较:

该代码示例旨在演示 ArcObjects 代码执行速度的可能差异,您在创建应用程序时可能需要考虑这些差异; 并非旨在作为 .NET 中 ArcObjects 性能的基准。代码示例在处理器速度为 3 GHz 的机器上运行,使用 .NET Framework 2.0,使用 Visual Studio。

在 C# 或 VB .NET 中创建一个 Windows 应用程序,添加一个带有单个按钮的窗口,然后在单击该按钮时,循环执行一些简单的 geometry 操作。

[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

前面的代码在 C# 和 VB .NET 中运行平均需要 30 秒。 为了进行比较,使用 Visual C++ 创建标准可执行文件 (EXE) (GetTickCount Windows API 调用替换了 .NET Framework TickCount 属性)。

等效的 Visual C++ 代码需要 5 秒的时间来执行,这比 .NET 快了大约 6 倍。 但是,执行数千次【这样执行速度非常快的小操作】是一种极端情况。 在这种极端情况下,COM 和 .NET 之间的【互操作】所花费的时间,很可能占大头而会主宰总运行时间。

示例 2

创建和填充地理数据库

请考虑创建和填充个人地理数据库的示例代码:创建个人地理数据库,然后导入包含示例数据的 shapefile ; 创建子类型并构建具有连通性规则的几何网络;还创建了一个复合关系类。

与 示例 1 相比,此代码包含了一个执行时间更长的操作。

在 C# 和 VB .NET 中,此代码平均需要 25 秒才能运行,而在 VB6 中,执行时间也平均为 25 秒。 考虑到【互操作层】所需的额外指令,.NET 代码如何才能同样快速地执行? VB6 代码由 Visual Basic 运行时执行,其效率可能低于 .NET Framework。

对于此示例,环境之间的性能没有差异。

回顾示例 1

在托管 C++ 中创建代理类

实际上,您可能需要执行,在 .NET 中会很耗时的操作(小操作的大迭代)。你必须接受性能打击吗? 不必要。您可以选择在托管 C++ 中编写代理类。

您可以在代理类中放置那些大量使用【互操作层】的 ArcObjects 代码。 然后,将代理类中此代码公开为公共方法,就可以从 C# 或 VB .NET 代码中调用这些方法。 由于只在调用该代理类的方法时才需要互操作层,因此可以大大减少执行时间。

例如,要执行前面的操作,您可以在托管 C++ 中创建一个代理类。 此类包含一个执行 geometry 操作的循环的函数。 请参见以下代码示例:

[C++]

#using "C:\Program Files (x86)\ArcGIS\DeveloperKit10.2\Dotnet\ESRI.ArcGIS.System.dll"
#using "C:\Program Files (x86)\ArcGIS\DeveloperKit10.2\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;
    }
  };
} 

可以使用如下代码从 .NET 应用程序调用上述函数(记得添加对先前创建的 .dll 的引用):

[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

通过使用 Managed C++ 编写的代理类,平均运行时间提高到 5 秒。

使用托管 C++ 的一个缺点是,用托管 C++ 编写的程序集不能使用 .NET 安全性的验证机制; 因此,它需要在受信任的环境中运行。 Visual C++ .NET 的未来版本可能会添加【可以改变这种情况的附加功能】。

提高性能的关键,是找出应用程序中的性能延迟,这可能是也可能不是【.NET与COM互操作的时间】。 对于大多数 ArcObjects 应用程序,使用 .NET 进行开发不会显着降低代码的性能。 如果您发现互操作是主要的延迟,则为这部分代码编写托管 C++ 代理类,可以作为问题的解决方案。

posted @ 2022-02-23 15:54  误会馋  阅读(84)  评论(0编辑  收藏  举报