3

我面临着使用 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。但它会那么慢吗?每秒 300 个项目,100% 核心负载为 3,2 GHz CPU。

上面代码的性能分析: 性能分析

4

2 回答 2

1

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

推荐阅读:IE + JavaScript 性能建议 - 第 1 部分

于 2013-02-02T22:17:18.163 回答
0

性能不佳的根源在于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
于 2013-02-03T09:09:44.417 回答