【问题标题】:HTML traversal is very slowHTML遍历很慢
【发布时间】:2018-12-20 07:07:42
【问题描述】:

我遇到过使用 C# 简单地遍历 MSHTML 元素的速度非常慢。这是通过 document.all 集合迭代三次的小例子。我们有空白的 WPF 应用程序和名为 Browser 的 WebBrowser 控件:

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();

        Browser.LoadCompleted += DocumentLoaded;
        Browser.Navigate("http://google.com");
    }

    private IHTMLElementCollection _items;

    private void DocumentLoaded(object sender, NavigationEventArgs e)
    {
        var dc = (HTMLDocument)Browser.Document;
        _items = dc.all;

        Test();
        Test();
        Test();
    }

    private void Test()
    {
        var sw = new Stopwatch();
        sw.Start();

        int i;
        for (i = 0; i < _items.length; i++)
        {
            _items.item(i);
        }

        sw.Stop();

        Debug.WriteLine("Items: {0}, Time: {1}", i, sw.Elapsed);
    }
}

输出是:

Items: 274, Time: 00:00:01.0573245
Items: 274, Time: 00:00:00.0011637
Items: 274, Time: 00:00:00.0006619

1 和 2 行之间的性能差异是可怕的。我尝试用非托管 C++ 和 COM 重写相同的代码,并且完全没有性能问题,非托管代码的运行速度快了 1200 倍。不幸的是,非托管不是一种选择,因为真正的项目比简单的迭代更复杂。

我了解到,运行时第一次为每个引用的 HTML 元素(即 COM 对象)创建 RCW。但它会那么慢吗?在 3.2 GHz CPU 的 100% 核心负载下每秒处理 300 个项目。

上面代码的性能分析:

【问题讨论】:

  • 您是否尝试过使用 Html Agility Pack 代替?
  • 不,因为这是第 3 方,我们不需要在项目中“解析”HTML,我们需要节点作为对象。
  • 我不明白其中的区别。 MSHTML 确实解析 HTML,而 Html Agility Pack 确实为您提供了节点作为对象。

标签: c# performance traversal mshtml


【解决方案1】:

使用 for each 而不是 document.all.item(index) 枚举所有元素集合(如果切换到 C++,请使用 IHTMLElementCollection::get__newEnum)。

推荐阅读:IE + JavaScript Performance Recommendations - Part 1

【讨论】:

    【解决方案2】:

    性能不佳的根源在于 MSHTML 互操作程序集中定义为 动态 对象的集合项。

    public interface IHTMLElementCollection : IEnumerable
    {
        ...
        [DispId(0)]
        dynamic item(object name = Type.Missing, object index = Type.Missing);
        ...
    }
    

    如果我们重写该接口使其返回 IDispatch 对象,那么滞后将消失。

    public interface IHTMLElementCollection : IEnumerable
    {
        ...
        [DispId(0)]
        [return: MarshalAs(UnmanagedType.IDispatch)]
        object item(object name = Type.Missing, object index = Type.Missing);
        ...
    }
    

    新输出:

    Items: 246, Time: 00:00:00.0034520
    Items: 246, Time: 00:00:00.0029398
    Items: 246, Time: 00:00:00.0029968
    

    【讨论】:

      猜你喜欢
      • 2018-01-19
      • 1970-01-01
      • 1970-01-01
      • 2014-11-14
      • 2018-01-07
      • 1970-01-01
      • 1970-01-01
      • 2013-11-16
      • 1970-01-01
      相关资源
      最近更新 更多