2009-12-21 2 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, две вещи, которые на самом деле выполняются очень точно, могут отличаться на 15 мс только из-за слегка отличающихся сторон округления граница. Поэтому, если у вас есть тесты, которые заканчиваются вокруг отметки 15-30 мс, либо запускайте больше итераций (чтобы ошибка округления 15 мс стала несущественной), либо используйте таймер с более высоким разрешением, например System.Diagnostics.Stopwatch (последний имеет несколько более удобный API тоже!). – itowlson

ответ

5

Это не (в основном) динамическое создание:. Это использование отражения (FieldInfo.SetValue), что делает Button3 и Button4 версии медленнее, чем когда звонки могут быть скомпилированы в

Возможный способ обойти это объявить интерфейс, с которым может компилироваться ваш код, и иметь динамический тип, реализующий этот интерфейс. Вы все равно возьмете небольшой экземпляр ударов и запросите динамический тип для этого интерфейса с помощью отражения, но после этого он должен быть таким же быстрым, как «статические» ссылки.

+2

Вы также можете обойти это, используя 'System.Expressions', так что вам нужно будет только один раз сделать отражение для всего вашего набора объектов. –

+1

Кроме того, если вы хотите использовать отражение и совершать много повторных вызовов, подумайте о создании делегата и использовании этого. http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx –

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; 
} 

Спасибо за помощь. Кстати, мы пришли сюда, используя ответы Джастина &. И GetInstanceInvoker - это класс Common.Reflection от Kenneth Xu.