2013-02-02 277 views
2

我面临的情况是,仅仅使用C#遍历MSHTML元素的速度非常慢。这里是通过document.all三次迭代的迭代的一个小例子。我们有空白WPF应用程序和WebBrowser控件命名的浏览器:HTML遍历速度很慢

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个项目,3,2 GHz CPU的100%核心负载。上面的代码的

性能分析: Performance analysis

+0

您是否尝试过使用HTML敏捷性包呢? – svick

+0

不,因为这是第三方,我们不需要在项目中'解析'HTML,所以我们需要节点作为对象。 – Dizzy

+4

我不明白区别。 MSHTML确实解析了HTML和Html Agility Pack确实会为您提供节点作为对象。 – svick

回答

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