2009-12-21 4 views
1

C#을 사용하여 동적으로 생성 된 유형의 인스턴스와 간단한 구조체의 인스턴스로 목록을 채울 때 성능에 큰 차이가 있음을 눈치 챘을 것입니다. 아래 코드에는 100,000 개 개체로 목록을 채우는 4 가지 방법이 포함되어 있습니다.동적으로 생성 된 유형의 성능

각있어서 다르게 수행

Button1에 300 밀리

참고 버튼 3 코드 & 4에서 온 15 밀리

Button2를 : 4 & 31 밀리

Button3을을 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

답변

5

아니에요 :.이 호출은 컴파일 할 때보다 단추 3과의 단추 버전의 느린하게 반사 (FieldInfo.SetValue)의 사용이다

이 주변에 가능한 방법은 코드가 컴파일 할 수있는 인터페이스를 선언하고 동적 유형이이 인터페이스를 구현하도록합니다. 리플렉션을 통해 해당 인터페이스의 동적 유형을 인스턴스화하고 쿼리하는 작은 히트를 취하지 만 이후에는 정적 참조만큼 빠릅니다.

+2

'System.Expressions'를 사용하여이 문제를 해결할 수도 있습니다. 따라서 전체 개체 세트에 대해 한 번만 반사시켜야합니다. –

+1

또한 리플렉션을 사용하고 반복적으로 여러 번 전화를 걸면 대표를 만들고이를 사용하는 것이 좋습니다. http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx –

1

간단한 답변 : 더 많은 코드가 객체를 생성 dynamicaly하기 위해 실행되고있다.

반사는 항상 유형을 정면으로 정의한 다음 해당 객체로 직접 작업하는 것보다 느리게 진행됩니다. 모든 것을 정면으로 지정하기보다는 런타임에 더 많은 작업을 수행하도록 요청하고 있습니다. 사용중인 Reflection 기능에 따라 코드가 느려집니다.

자세한 내용을 보려면 코드에서 생성되는 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는 Kenneth Xu의 Common.Reflection 클래스에서 가져온 것입니다.

관련 문제