使用C#时,我注意到在使用动态生成类型的实例与简单结构填充列表时,性能存在显着差异。下面的代码包含4种不同的方法,用于填充包含100,000个对象的列表。
每种方法的表现都不同:
Button1:15毫秒
按钮2: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;
}
答案 0 :(得分:5)
它(主要)不是动态创建:它使用了反射(FieldInfo.SetValue),使得button3和button4版本比编译调用时更慢。
一种可能的方法是声明代码可以编译的接口,并使动态类型实现此接口。您仍然会通过反射实例化并查询该接口的动态类型,但之后它应该与“静态”引用一样快。
答案 1 :(得分:1)
简单回答:正在执行更多代码以动态创建对象。
反射总是比预先定义类型然后直接使用该对象慢。您要求运行时为您做更多的工作,而不是预先指定所有内容。根据您使用的反射功能......您的代码会变慢。
如果需要详细信息,请检查代码生成的IL。这应该给你整个故事。
答案 2 :(得分: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;
}
感谢您的帮助。顺便说一句,我们使用贾斯汀的&amp;马克的答案。 GetInstanceInvoker来自Kenneth Xu的Common.Reflection类。