Как оптимизировать время выполнения для преобразования datatable в коллекцию?

#c# #performance #collections #loops

#c# #Производительность #Коллекции #циклы

Вопрос:

У меня есть база данных, к которой я должен подключиться через odbc.

Выборка данных выполняется приложением. 2 минуты. и результирующая таблица данных содержит 350000 записей.

Я пытаюсь преобразовать таблицу данных в этот объектный граф. Результирующий набор не имеет первичного ключа, первичный ключ указывается в представлении, из которого я извлекаю данные.

 public class PriceCurve
{
    public PriceCurve(DataTable dt)
    {
        this.Id = int.Parse(dt.AsEnumerable().First()["ID"].ToString());
        this.Prices = new List<Price>();
        GetPrices(dt);
    }

    public int Id { get; private set; }
    public IList<Price> Prices { get; set; }

    private void GetPrices(DataTable dt)
    {
        foreach (DataColumn column in dt.Columns)
        {
            switch (this.GetPriceProviderType(column)) // parses ColumnName to Enum
            {
                case Price.PriceProvider.A:
                    {
                        this.Prices.Add(new Price(Price.PriceProvider.A, dt.AsEnumerable()));
                    }

                    break;
                case Price.PriceProvider.B:
                    {
                        this.Prices.Add(new Price(Price.PriceProvider.B, dt.AsEnumerable()));
                    }

                    break;
            }
        }

    public class Price
    {
        public enum PriceProvider
        {
            A, B
        }

        public Price(PriceProvider type, IEnumerable<DataRow> dt)
        {
            this.Type = type;
            this.TradingDates = new List<TradingDate>();
            this.GetTradingDates(type, dt);
        }

        public IList<TradingDate> TradingDates { get; set; }
        public PriceProvider Type { get; set; }

        private void GetTradingDates(PriceProvider type, IEnumerable<DataRow> dt)
        {
            var data = dt.Select(column => column["TRADING_DATE"]).Distinct();

            foreach (var date in data)
            {
                this.TradingDates.Add(new TradingDate(date.ToString(), type, dt));
            }
        }

        public class TradingDate
        {
            public TradingDate(string id, PriceProvider type, IEnumerable<DataRow> dt)
            {
                this.Id = id;
                this.DeliveryPeriodValues = new Dictionary<int, double?>();
                this.GetDeliveryPeriodValues(type, dt);
            }

            public string Id { get; set; }
            public IDictionary<int, double?> DeliveryPeriodValues { get; set; }

            private void GetDeliveryPeriodValues(PriceProvider type, IEnumerable<DataRow> dt)
            {

                foreach (var row in dt.Where(column => column["TRADING_DATE"].ToString() == this.Name))
                {
                    try
                    {
                        this.DeliveryPeriodValues.Add(
                            int.Parse(row["DELIVERY_PERIOD"].ToString()),
                            double.Parse(row[Enum.GetName(typeof(Price.PriceProvider), type)].ToString()));
                    }
                    catch (FormatException e)
                    {
                        this.DeliveryPeriodValues.Add(
                            int.Parse(row["DELIVERY_PERIOD"].ToString()),
                            null);
                    }
                }    
            }
        }
    }
  

я создаю один объект, который содержит список с двумя объектами. Каждый из этих двух объектов содержит список из 1000 объектов. Каждый из этих 1000 объектов содержит словарь с 350 парами.

Это либо приводит к сбою Visual Studio 2010 во время отладки, завершается сбоем из-за OutOfMemory, либо занимает минуты (неприемлемо) для выполнения.

Каков наилучший подход к этой проблеме. я новичок в c # и не знаю, как оптимизировать цикл по этим огромным данным или моему объектному графу. Приветствуется любая помощь.

Ответ №1:

Это либо приводит к сбою Visual Studio 2010 во время отладки, завершается сбоем из-за OutOfMemory, либо занимает минуты (неприемлемо) для выполнения.

Ты заставил меня смеяться. Действительно.

  • 350.000 узлов — это сложная задача на 32-разрядной машине с .NET. Добавьте немного накладных расходов, и вы мертвы. Используйте объекты, а не таблицу adata, которая СИЛЬНО разрушает память.

  • занимает минуты — это в значительной степени ваше решение / программирование. Используйте список объектов, а не таблицу данных. Используйте профилировщик. Не допускайте ошибок новичка, как:

var data = dt.Select(столбец => column[«TRADING_DATE»]).Distinct();

В этом нет необходимости, разберитесь с удвоениями позже в коде. Distinct стоит дорого. Профилируйте его.

foreach (переменная строка в dt.Где(столбец => column[«TRADING_DATE»].toString() == this.Name ))

Это 350.000 поисков строк по имени, чтобы получить индекс столбца, по сравнению с большим количеством tostring.

Получите профилировщик и узнайте, на что именно вы тратите свое время. Пожалуйста, избавьтесь от таблицы и используйте objects — DataTable занимает много памяти и МЕДЛЕННЕЕ по сравнению со списком объектов. И да, это займет минуты. Основные причины:

  • Ваше программирование. Не позор. Просто учитесь, переходите к объектам / структурам ПРЯМО СЕЙЧАС.
  • ODBC. Требуется время, чтобы просто загрузить данные, особенно если вы не обрабатываете swhile loading (DataReader), а ждете, пока все загрузится, а ODBC НЕ быстрый. 350.000 строк, хорошая сеть, прямой SQL Server занимает, возможно, 30 секунд — на той же машине меньше.

Комментарии:

1. итак, я должен в основном использовать средство чтения данных и работать напрямую с каждым объектом [], который возвращается средством чтения. GetNames(новый объект[])

2. Нет, это массив, а не объект передачи adata. Вы должны немедленно перенести их в реальные объекты. Вы знаете, тип с переменными, свойствами.

3. Да, я слышал об этой штуковине ;). таким образом, наилучшим подходом было бы что-то вроде { var neededCol = reader. GetOrdinal(«TRADING_DATE»); var myObj = новая кривая цен(); while (reader.Read()) { myObj. MyProperty = reader. GetValue(neededCol);}}

4. В значительной степени. Это выводит GetOrdinal из цикла (да, это не бесплатно — это поиск по хэш-таблице), а затем вы работаете с небольшими эффективными объектами. Затем СБРОСЬТЕ ODBC — либо используйте OLEDB, либо драйвер natce. Более поздний вариант существует практически для каждой крупной базы данных. И если вам все еще нужно больше памяти, перейдите на 64-разрядную ОС 😉

5. Я думаю, что огромным узким местом является использование вами таблиц данных. Они тяжелые и неэффективные.. Начните сначала. перепишите с помощью конкретного класса, представляющего каждую запись в вашей таблице (ах). Используйте DataReaders для получения данных каждой строки и преобразования их в экземпляр этого класса. Напишите метод, который возвращает список этого класса для представления всей таблицы или результатов ваших запросов.. @TomTom прав! Отобразите результаты на странице. Используйте запрос, который может получить x количество элементов, начинающихся с элемента y. (Подкачка в SQL: social.technet.microsoft.com/wiki/contents/articles /… )