1

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

每种方法的执行方式不同:

按钮 1:15 毫秒

按钮 2:31 毫秒

按钮 3 和 4:300 毫秒

请注意,按钮 3 和 4 的代码来自此主题

谁能解释为什么动态创建的对象更慢?

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

3 回答 3

5

这不是(主要)动态创建:它是使用反射 (FieldInfo.SetValue) 使 button3 和 button4 版本比可以编译调用时慢。

解决此问题的一种可能方法是声明一个您的代码可以编译的接口,并让动态类型实现此接口。您仍然会通过反射来实例化和查询该接口的动态类型,但之后它应该与“静态”引用一样快。

于 2009-12-21T19:38:33.673 回答
2

简单答案:正在执行更多代码以动态创建对象。

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

如果您需要详细信息,请检查您的代码生成的 IL。那应该给你整个故事。

于 2009-12-21T19:38:23.407 回答
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 类。

于 2010-01-04T05:00:33.573 回答