您的位置:首页 > 其它

【Winform】ListView虚拟模式与普通模式的性能对比 -- 深入分析

2014-01-17 08:35 453 查看
本文对我的上一篇文章ListView虚拟模式与普通模式的性能对比进行了更深入的分析。上一篇文章可视为如何使用虚拟模式,这篇文章分析了虚拟模式的原理。

1、 从代码上分析:

普通模式使用的是Collection.Add()这种方法来添加数据。

虚拟模式使用的是在事件RetrieveVirtualItem中使用e.Item = Collection[e.ItemIndex];这种方法来添加数据。

貌似看不出什么端倪,那么我们请出IL DSAM反编译生成的exe文件来从IL上分析。

2、 从IL分析:(IL代码部分省略,只显示关键部分。)

普通模式Collection.Add()和虚拟模式e.Item = Collection[e.ItemIndex]

普通模式IL代码如图:

.method private hidebysig instance void  Btn_GenerateClick(object sender,
class [mscorlib]System.EventArgs e) cil managed
{
// Code size       222 (0xde)
.maxstack  5
.locals init (int32 V_0,
int32 V_1,
int32 V_2,
int32 V_3,
int32 V_4,
int32 V_5)
IL_0000:  ldarg.0
IL_0001:  ldnull
IL_0002:  stfld      class [System.Windows.Forms]System.Windows.Forms.ListViewItem[] ListViewVirtualMode.ListViewWithoutVirtualMode::_itemsCacheCollection
IL_0007:  ldarg.0
IL_0008:  ldfld      class [System.Windows.Forms]System.Windows.Forms.ListView ListViewVirtualMode.ListViewWithoutVirtualMode::listView1
IL_000d:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.ListView::Clear()
IL_0012:  ldarg.0
IL_0013:  ldfld      class [System.Windows.Forms]System.Windows.Forms.TextBox ListViewVirtualMode.ListViewWithoutVirtualMode::textBox_RowTotal
IL_0018:  callvirt   instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text()
IL_001d:  call       int32 [mscorlib]System.Convert::ToInt32(string)
IL_0022:  stloc.0
IL_0023:  ldarg.0
IL_0024:  ldloc.0
IL_0025:  newarr     [System.Windows.Forms]System.Windows.Forms.ListViewItem
IL_002a:  stfld      class [System.Windows.Forms]System.Windows.Forms.ListViewItem[] ListViewVirtualMode.ListViewWithoutVirtualMode::_itemsCacheCollection
IL_002f:  ldarg.0
IL_0030:  ldfld      class [System.Windows.Forms]System.Windows.Forms.CheckBox ListViewVirtualMode.ListViewWithoutVirtualMode::checkBox_PictureCol
IL_0035:  callvirt   instance bool [System.Windows.Forms]System.Windows.Forms.CheckBox::get_Checked()
IL_003a:  brtrue.s   IL_006c
IL_003c:  ldc.i4.0
IL_003d:  stloc.1
IL_003e:  br.s       IL_0066
IL_0040:  ldarg.0
IL_0041:  ldfld      class [System.Windows.Forms]System.Windows.Forms.ListViewItem[] ListViewVirtualMode.ListViewWithoutVirtualMode::_itemsCacheCollection
IL_0046:  ldloc.1
IL_0047:  ldstr      "item"
IL_004c:  ldloc.1
IL_004d:  ldc.i4.1
IL_004e:  add
IL_004f:  stloc.3
IL_0050:  ldloca.s   V_3
IL_0052:  call       instance string [mscorlib]System.Int32::ToString()
IL_0057:  call       string [mscorlib]System.String::Concat(string,
string)
IL_005c:  newobj     instance void [System.Windows.Forms]System.Windows.Forms.ListViewItem::.ctor(string)
IL_0061:  stelem.ref
IL_0062:  ldloc.1
IL_0063:  ldc.i4.1
IL_0064:  add
IL_0065:  stloc.1
IL_0066:  ldloc.1
IL_0067:  ldloc.0
IL_0068:  blt.s      IL_0040
IL_006a:  br.s       IL_00c7
IL_006c:  ldc.i4.0
IL_006d:  stloc.2
IL_006e:  br.s       IL_00c3
IL_0070:  ldloc.2
IL_0071:  ldc.i4.2
IL_0072:  rem
IL_0073:  brtrue.s   IL_009b
IL_0075:  ldarg.0
IL_0076:  ldfld      class [System.Windows.Forms]System.Windows.Forms.ListViewItem[] ListViewVirtualMode.ListViewWithoutVirtualMode::_itemsCacheCollection
IL_007b:  ldloc.2
IL_007c:  ldstr      "item"
IL_0081:  ldloc.2
IL_0082:  ldc.i4.1
IL_0083:  add
IL_0084:  stloc.s    V_4
IL_0086:  ldloca.s   V_4
IL_0088:  call       instance string [mscorlib]System.Int32::ToString()
IL_008d:  call       string [mscorlib]System.String::Concat(string,
string)
IL_0092:  ldc.i4.0
IL_0093:  newobj     instance void [System.Windows.Forms]System.Windows.Forms.ListViewItem::.ctor(string,
int32)
IL_0098:  stelem.ref
IL_0099:  br.s       IL_00bf
IL_009b:  ldarg.0
IL_009c:  ldfld      class [System.Windows.Forms]System.Windows.Forms.ListViewItem[] ListViewVirtualMode.ListViewWithoutVirtualMode::_itemsCacheCollection
IL_00a1:  ldloc.2
IL_00a2:  ldstr      "item"
IL_00a7:  ldloc.2
IL_00a8:  ldc.i4.1
IL_00a9:  add
IL_00aa:  stloc.s    V_5
IL_00ac:  ldloca.s   V_5
IL_00ae:  call       instance string [mscorlib]System.Int32::ToString()
IL_00b3:  call       string [mscorlib]System.String::Concat(string,
string)
IL_00b8:  ldc.i4.1
IL_00b9:  newobj     instance void [System.Windows.Forms]System.Windows.Forms.ListViewItem::.ctor(string,
int32)
IL_00be:  stelem.ref
IL_00bf:  ldloc.2
IL_00c0:  ldc.i4.1
IL_00c1:  add
IL_00c2:  stloc.2
IL_00c3:  ldloc.2
IL_00c4:  ldloc.0
IL_00c5:  blt.s      IL_0070
IL_00c7:  ldarg.0
IL_00c8:  ldfld      class [System.Windows.Forms]System.Windows.Forms.ListView ListViewVirtualMode.ListViewWithoutVirtualMode::listView1
IL_00cd:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.ListView/ListViewItemCollection [System.Windows.Forms]System.Windows.Forms.ListView::get_Items()
IL_00d2:  ldarg.0
IL_00d3:  ldfld      class [System.Windows.Forms]System.Windows.Forms.ListViewItem[] ListViewVirtualMode.ListViewWithoutVirtualMode::_itemsCacheCollection
IL_00d8:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.ListView/ListViewItemCollection::AddRange(class [System.Windows.Forms]System.Windows.Forms.ListViewItem[])
IL_00dd:  ret
} // end of method ListViewWithoutVirtualMode::Btn_GenerateClick
其中最后部分IL_00d8:System.Windows.Forms.ListView/ListViewItemCollection::AddRange(class
[System.Windows.Forms]System.Windows.Forms.ListViewItem[])为普通模式添加数据的代码

虚拟模式IL代码如图:

.method private hidebysig instance void  listView1_RetrieveVirtualItem(object sender,
class [System.Windows.Forms]System.Windows.Forms.RetrieveVirtualItemEventArgs e) cil managed
{
// Code size       54 (0x36)
.maxstack  3
.locals init (int32 V_0)
IL_0000:  ldarg.0
IL_0001:  ldfld      class [System.Windows.Forms]System.Windows.Forms.ListViewItem[] ListViewVirtualMode.ListViewWithVirtualMode::_itemsCacheCollection
IL_0006:  brfalse.s  IL_001c
IL_0008:  ldarg.2
IL_0009:  ldarg.0
IL_000a:  ldfld      class [System.Windows.Forms]System.Windows.Forms.ListViewItem[] ListViewVirtualMode.ListViewWithVirtualMode::_itemsCacheCollection
IL_000f:  ldarg.2
IL_0010:  callvirt   instance int32 [System.Windows.Forms]System.Windows.Forms.RetrieveVirtualItemEventArgs::get_ItemIndex()
IL_0015:  ldelem.ref
IL_0016:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.RetrieveVirtualItemEventArgs::set_Item(class [System.Windows.Forms]System.Windows.Forms.ListViewItem)
IL_001b:  ret
IL_001c:  ldarg.2
IL_001d:  ldarg.2
IL_001e:  callvirt   instance int32 [System.Windows.Forms]System.Windows.Forms.RetrieveVirtualItemEventArgs::get_ItemIndex()
IL_0023:  stloc.0
IL_0024:  ldloca.s   V_0
IL_0026:  call       instance string [mscorlib]System.Int32::ToString()
IL_002b:  newobj     instance void [System.Windows.Forms]System.Windows.Forms.ListViewItem::.ctor(string)
IL_0030:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.RetrieveVirtualItemEventArgs::set_Item(class [System.Windows.Forms]System.Windows.Forms.ListViewItem)
IL_0035:  ret
} // end of method ListViewWithVirtualMode::listView1_RetrieveVirtualItem
其中IL_0030:System.Windows.Forms.RetrieveVirtualItemEventArgs::set_Item为虚拟模式添加数据的代码

接下来需要请出ILSpy或者Reflector来帮我们分析这两句关键代码的内在不同。(省略了众多步分析过程)

虚拟模式IL_0030:System.Windows.Forms.RetrieveVirtualItemEventArgs::set_Item


[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public void set_Item(ListViewItem value)
{
this.item = value;
}


相当于只创建了一个class A(class A与class B的结构完全相同), 然后不停的把新的class B赋给class A,以覆盖class A中原有的数据,就有点像 int x = 0; x += 1; 只创建了一个x对象,然后不停的覆盖原有的x值。至于index是由RetrieveVirtualItemEventArgs类内部做的处理,详情见代码:

namespace System.Windows.Forms
{
using System;
using System.Runtime;

public class RetrieveVirtualItemEventArgs : EventArgs
{
private ListViewItem item;
private int itemIndex;

[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public RetrieveVirtualItemEventArgs(int itemIndex)
{
this.itemIndex = itemIndex;
}

public ListViewItem Item
{
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
get
{
return this.item;
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
set
{
this.item = value;
}
}

public int ItemIndex
{
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
get
{
return this.itemIndex;
}
}
}
}


普通模式IL_00d8:System.Windows.Forms.ListView/ListViewItemCollection::AddRange(class
[System.Windows.Forms]System.Windows.Forms.ListViewItem[])
请注意括号中的红色部分,这部分是new 出来了N个ListViewItem类,并使用其默认的构造函数。



public void AddRange(ListViewItem[] items)
{
if (items == null)
{
throw new ArgumentNullException("items");
}
this.InnerList.AddRange(items);
}
using System;
using System.Collections;
using System.Reflection;
using System.Windows.Forms;

internal interface IInnerList
{
ListViewItem Add(ListViewItem item);
void AddRange(ListViewItem[] items);
void Clear();
bool Contains(ListViewItem item);
void CopyTo(Array dest, int index);
IEnumerator GetEnumerator();
int IndexOf(ListViewItem item);
ListViewItem Insert(int index, ListViewItem item);
void Remove(ListViewItem item);
void RemoveAt(int index);

int Count { get; }

ListViewItem this[int index] { get; set; }

bool OwnerIsDesignMode { get; }

bool OwnerIsVirtualListView { get; }
}
由于AddRange并未对Index作出特别处理,而使用的是ListViewItem自带的index,结果最终导致产生了N个对象。

总结

至于为什么微软这么做不得而知,但是这点给我了个启示,如果两个class/struct/array结构完全相同,那么是否可以通过另外处理index的方法来把不同的N个class/struct/array使用数据覆盖的方式来进行赋值,只需要new出一个新对象,而不是产生N个全新的对象再进行处理,这样程序的运行效率会大幅度的提升。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: