HTML遍历非常缓慢

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

上述代码的性能分析: 绩效分析

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

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

性能不佳的原因是在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