2009-12-21 80 views
1

使用C#时,我注意到使用动态生成类型的实例与简单结构填充列表时的性能差异。下面的代码包含4种用于使用100,000个对象填充列表的不同方法。动态创建类型的性能

每种方法进行不同的:

Button1的:15毫秒

将Button2:31毫秒

将Button3 & 4:300毫秒

注意,对于按钮3的代码& 4从来到this topic

任何人都可以解释为什么动态创建的对象更慢?

public struct DataRow 
    { 
     public double t; 
     public double vf; 
     public double im; 

     public double T { get { return t; } set { t = value; } } 
     public double Vf { get { return vf; } set { vf = value; } } 
     public double Im { get { return im; } set { im = value; } } 
    } 

    //Use struct defined above 
    private void button1_Click(object sender, EventArgs e) 
    { 
     int n = 0; 

     //adding rows 
     List<DataRow> myTable = new List<DataRow>(); 
     DataRow myRow = new DataRow(); 

     start = DateTime.Now; 

     while (n < 100000) 
     { 
      myRow.T = n * 1.0; 
      myRow.Vf = 2.0; 
      myRow.Im = 4.0; 

      myTable.Add(myRow); 

      n++; 
     } 
     end = DateTime.Now; 
     System.TimeSpan diff = end.Subtract(start); 
     label2.Text = diff.Seconds.ToString(); 
     label4.Text = diff.Milliseconds.ToString(); 

     dataGridView1.DataSource = myTable; 
    } 

    //define the list as it is done on buttons 3 & 4 but use the static struct 
    private void button2_Click(object sender, EventArgs e) 
    { 
     Type myType = typeof(DataRow); 

     Type listType = typeof(List<>); 

     Type myListType = listType.MakeGenericType(myType); 

     IList myTable = (IList)Activator.CreateInstance(myListType); 

     DataRow bRow = new DataRow(); 

     int n = 0; 
     start = DateTime.Now; 
     while (n < 100000) 
     { 
      bRow.t = n * 1.0; 
      bRow.vf = 2.0; 
      bRow.im = 4.0; 
      myTable.Add(bRow); 

      n++; 
     } 
     end = DateTime.Now; 
     System.TimeSpan diff = end.Subtract(start); 
     label2.Text = diff.Seconds.ToString(); 
     label4.Text = diff.Milliseconds.ToString(); 
     dataGridView1.DataSource = myTable; 

    } 

    //Create assy at runtime and load dll 
    private void button3_Click(object sender, EventArgs e) 
    { 
     Type myType = CreateDynRow(); 
     Assembly myAssy = Assembly.LoadFrom("DynaRowAssy.dll"); 
     Type myRow = myAssy.GetType("DynaRow"); 

     Type listType = typeof(List<>); 

     Type myListType = listType.MakeGenericType(myRow); 

     IList myTable = (IList)Activator.CreateInstance(myListType); 

     FieldInfo piT = myRow.GetField("t"); 
     FieldInfo piVf = myRow.GetField("vf"); 
     FieldInfo piIm = myRow.GetField("im"); 

     ValueType aRow = (ValueType)Activator.CreateInstance(myRow); 

     int n = 0; 
     start = DateTime.Now; 
     while (n < 100000) 
     { 
      piT.SetValue(aRow, 1 * n); 
      piVf.SetValue(aRow, 2.0); 
      piIm.SetValue(aRow, 4.0); 
      myTable.Add(aRow); 

      n++; 
     } 
     end = DateTime.Now; 
     System.TimeSpan diff = end.Subtract(start); 
     label2.Text = diff.Seconds.ToString(); 
     label4.Text = diff.Milliseconds.ToString(); 
     dataGridView1.DataSource = myTable; 
    } 

    //create assy at runtime in memory 
    private void button4_Click(object sender, EventArgs e) 
    { 
     //build the assembly 
     Type myType = CreateDynRow(); 

     Type listType = typeof(List<>); 

     Type myListType = listType.MakeGenericType(myType); 

     IList myTable = (IList)Activator.CreateInstance(myListType); 

     FieldInfo piT = myType.GetField("t"); 
     FieldInfo piVf = myType.GetField("vf"); 
     FieldInfo piIm = myType.GetField("im"); 

     ValueType aRow = (ValueType)Activator.CreateInstance(myType); 

     int n = 0; 
     start = DateTime.Now; 
     while (n < 100000) 
     { 
      piT.SetValue(aRow, 1 * n); 
      piVf.SetValue(aRow, 2.0); 
      piIm.SetValue(aRow, 4.0); 
      myTable.Add(aRow); 

      n++; 
     } 
     end = DateTime.Now; 
     System.TimeSpan diff = end.Subtract(start); 
     label2.Text = diff.Seconds.ToString(); 
     label4.Text = diff.Milliseconds.ToString(); 
     dataGridView1.DataSource = myTable; 
    } 
+5

只是一个提示:DateTime的分辨率大约是15毫秒,所以当你使用DateTime.Now来计时的时候,两个实际上执行得非常相似的事情可能会出现不同的长达15ms,只是由于舍入的略有不同边界。因此,如果您的基准测试在15-30ms左右运行,则要么运行更多迭代(以便15ms舍入误差变得微不足道),要么使用System.Diagnostics.Stopwatch等更高分辨率的计时器(后者稍微更方便一些API也是!)。 – itowlson 2009-12-21 19:43:10

回答

5

这不是(主要)动态创建。它是利用反射(FieldInfo.SetValue)才使得时相比,呼叫可以被编译慢BUTTON3和将Button4版本

对此的一种可能的方式是声明你的代码可以编译的接口,并且有动态类实现这个接口。您仍然会通过反射实例化并查询该接口的动态类型,但在此之后它应该与“静态”引用一样快。

+2

你也可以使用'System.Expressions'来解决这个问题,所以你只需要为整个对象做一次反射。 – 2009-12-21 19:56:10

+1

另外,如果您要使用反射并进行多次重复调用,请考虑创建一个委托并使用它。 http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx – 2009-12-22 17:28:15

1

简单的答案:正在执行更多的代码dynamicaly创建对象。

反射总是比预先定义类型慢,然后直接使用该对象。你要求运行时为你做更多的工作,而不是事先指定一切。根据您使用的反射功能,您的代码会变慢。

如果您需要具体细节,请检查您的代码生成的IL。这应该给你整个故事。

0

这是我们想出的。它几乎与静态定义的情况一样快:

// Dynamically create DataRow derived from ValueType, 
// List of DataRows, 
// Delegates to properties 
// 

private void button4_Click(object sender, EventArgs e) 
{ 
    Type myType = CreateDynRow(); // dynamic version of DataRow, see above 
    Type myListType = typeof(List<>).MakeGenericType(myType); 
    IList myTable = (IList)Activator.CreateInstance(myListType); 
    ValueType myRowBuffer = (ValueType)Activator.CreateInstance(myType); 

    var mySet_TDelegate = myRowBuffer.GetInstanceInvoker<Action<Double>>("set_T"); 
    var mySet_ImDelegate = myRowBuffer.GetInstanceInvoker<Action<Double>>("set_Im"); 
    var mySet_VfDelegate = myRowBuffer.GetInstanceInvoker<Action<Double>>("set_Vf"); 

    stopWatch.Reset(); 
    stopWatch.Start(); 
    for (int n = 0; n < rowCount; n++) 
    { 
     mySet_TDelegate(1.0 * n); 
     mySet_ImDelegate(4.0); 
     mySet_VfDelegate(2.0); 

     myTable.Add(myRowBuffer); 
    } 
    stopWatch.Stop(); 
    label1.Text = String.Format("{0}", stopWatch.ElapsedMilliseconds); 

    dataGridView1.DataSource = myTable; 
} 

感谢您的帮助。顺便说一下,我们使用Justin的&马克的答案来到这里。 GetInstanceInvoker来自Kenneth Xu的Common.Reflection类。