HTML遍历非常慢

时间:2013-02-02 20:40:14

标签: c# performance traversal mshtml

我遇到过简单地使用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。

上述代码的性能分析: Performance analysis

2 个答案:

答案 0 :(得分:1)

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

建议阅读:IE + JavaScript Performance Recommendations - Part 1

答案 1 :(得分: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