Производительность динамически создаваемого типа - PullRequest
1 голос
/ 21 декабря 2009

Используя C #, я заметил значительную разницу в производительности при заполнении списка экземплярами динамически генерируемого типа по сравнению с простой структурой. Приведенный ниже код включает 4 различных метода для заполнения списка 100 000 объектов.

Каждый метод работает по-разному:

Кнопка1: 15 миллисекунд

Кнопка2: 31 миллисекунда

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

Ответы [ 3 ]

5 голосов
/ 21 декабря 2009

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

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

1 голос
/ 21 декабря 2009

Простой ответ: Выполняется больше кода для динамического создания объекта.

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

Если вам нужны подробности, проверьте IL, который генерирует ваш код. Это должно дать вам всю историю.

0 голосов
/ 04 января 2010

Вот что мы придумали. Это почти так же быстро, как статически определенный случай:

// 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 Кеннета Сюй.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...