#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 /… )