Как избежать нехватки оперативной памяти при одновременной обработке данных?C#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Как избежать нехватки оперативной памяти при одновременной обработке данных?

Сообщение Anonymous »

У меня возникла проблема с одновременной обработкой данных. На моем компьютере быстро заканчивается оперативная память. Есть какие-нибудь советы о том, как исправить мою параллельную реализацию?

Общий класс:

Код: Выделить всё

public class CalculationResult
{
public int Count { get; set; }
public decimal[] RunningTotals { get; set; }

public CalculationResult(decimal[] profits)
{
this.Count = 1;
this.RunningTotals = new decimal[12];
profits.CopyTo(this.RunningTotals, 0);
}

public void Update(decimal[] newData)
{
this.Count++;

// summ arrays
for (int i = 0; i < 12; i++)
this.RunningTotals[i] = this.RunningTotals[i] + newData[i];
}

public void Update(CalculationResult otherResult)
{
this.Count += otherResult.Count;

// summ arrays
for (int i = 0; i < 12; i++)
this.RunningTotals[i] = this.RunningTotals[i] + otherResult.RunningTotals[i];
}
}
Одноядерная реализация кода следующая:

Код: Выделить всё

Dictionary combinations = new Dictionary();
foreach (var i in itterations)
{
// do the processing
// ..
string combination = "1,2,3,4,42345,52,523"; // this is determined during the processing

if (combinations.ContainsKey(combination))
combinations[combination].Update(newData);
else
combinations.Add(combination, new CalculationResult(newData));
}
Многоядерная реализация:

Код: Выделить всё

ConcurrentBag results = new ConcurrentBag();
Parallel.ForEach(itterations, (i, state) =>
{
Dictionary combinations = new Dictionary();
// do the processing
// ..
// add combination to combinations -> same logic as in single core implementation
results.Add(combinations);
});
Dictionary combinationsReal = new Dictionary();
foreach (var item in results)
{
foreach (var pair in item)
{
if (combinationsReal.ContainsKey(pair.Key))
combinationsReal[pair.Key].Update(pair.Value);
else
combinationsReal.Add(pair.Key, pair.Value);
}
}
Проблема, с которой я столкнулся, заключается в том, что почти каждый словарь комбинаций заканчивается записывает[/b] в него, что в среднем потребляет

Код: Выделить всё

400 [MB]
RAM[/b] память.

Теперь в одноядерной реализации существует только один такой словарь. Все проверки выполняются по одному словарю. Но это медленный подход, и я хочу использовать многоядерную оптимизацию.

В многоядерной реализации создается экземпляр ConcurrentBag, который содержит все комбинации< /код> словари. Как только многопоточная работа завершена — все словари объединяются в один. Этот подход хорошо работает для небольшого количества одновременных итераций. Например, за 4 итерации мое использование ОЗУ составило

Код: Выделить всё

~ 1.5 [GB]. Проблема возникает, когда я устанавливаю полное количество параллельных итераций, а именно 200! Никакого объема оперативной памяти ПК
не хватит, чтобы вместить все словари с миллионом записей в каждом!

Я думал об использовании ConcurrentDictioanary, пока не нашел Выяснилось, что метод «TryAdd» не гарантирует целостность добавленных данных в моей ситуации, так как мне также необходимо запускать обновления текущих итогов.

Единственный настоящий многопоточный метод вариант — вместо добавления всех комбинаций в словарь - это сохранить их в какой-нибудь БД. Тогда агрегирование данных будет выполняться с помощью одного оператора SQL select с предложением group by... но мне не нравится идея создания временной таблицы и запуска экземпляра БД только для этого.

Есть ли способ одновременной обработки данных без нехватки оперативной памяти?[/b]

РЕДАКТИРОВАТЬ:
Может быть, настоящий вопрос должно было быть - как сделать обновление RunningTotals потокобезопасным при использовании ConcurrentDictionary? Я только что столкнулся с этой темой с аналогичной проблемой с ConcurrentDictionary, но моя ситуация кажется более сложной, поскольку у меня есть массив, который необходимо обновить. Я все еще изучаю этот вопрос.

EDIT2: Вот рабочее решение с ConcurrentDictionary. Все, что мне нужно было сделать, это добавить блокировку для ключа словаря.

Код: Выделить всё

ConcurrentDictionary combinations = new ConcurrentDictionary();
Parallel.ForEach(itterations, (i, state) =>
{
// do the processing
// ..
string combination = "1,2,3,4,42345,52,523"; // this is determined during the processing

if (combinations.ContainsKey(combination)) {
lock(combinations[combination])
combinations[combination].Update(newData);
}
else
combinations.TryAdd(combination, new CalculationResult(newData));
});
Время выполнения однопоточного кода составляет 1 м 48 с, тогда как время выполнения этого решения составляет 1 м 7 с для 4 итераций (увеличение производительности на 37 %). . Мне все еще интересно, будет ли подход SQL быстрее с миллионами записей? Возможно, завтра я проверю его и обновлю.

Редактировать 3: Для тех из вас, кому интересно, что не так с обновлениями ConcurrentDictionary значение — запустите этот код с блокировкой и без нее.

Код: Выделить всё

public class Result
{
public int Count { get; set; }
}

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start");

List keys = new List();
for (int i = 0; i < 100; i++)
keys.Add(i);

ConcurrentDictionary dict = new ConcurrentDictionary();
Parallel.For(0, 8, i =>
{
foreach(var key in keys)
{
if (dict.ContainsKey(key))
{
//lock (dict[key]) // uncomment this
dict[key].Count++;
}
else
dict.TryAdd(key, new Result());
}
});

// any output here is incorrect behavior. best result = no lines
foreach (var item in dict)
if (item.Value.Count != 7) { Console.WriteLine($"{item.Key}; {item.Value.Count}"); }

Console.WriteLine($"Finish");
Console.ReadKey();
}
}
Редактировать 4: После проб и ошибок мне не удалось оптимизировать подход SQL. Это оказалась худшая идея :) Я использовал базу данных SQL Lite. В памяти и в файле. С транзакциями и повторно используемыми параметрами команд SQL. Из-за огромного количества записей, которые нужно было вставить - производительности не хватает. Агрегация данных - самая простая часть, но только для вставки 4 миллионов строк требуется огромное количество времени, я даже не могу представить, как можно эффективно обработать 240 миллионов данных.. Пока (и тоже странно) , подход ConcurrentBag кажется самым быстрым на моем ПК. Далее следует подход ConcurrentDictionary. Однако ConcurrentBag требует немного больше памяти. Благодаря работе @Alisson — теперь его можно использовать для большего набора итераций!

Подробнее здесь: https://stackoverflow.com/questions/479 ... roccessing
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Как избежать нехватки памяти при чтении сложного PDF-файла через iText7?
    Anonymous » » в форуме JAVA
    0 Ответы
    31 Просмотры
    Последнее сообщение Anonymous
  • Как избежать «CUDA нехватки памяти» в PyTorch
    Anonymous » » в форуме Python
    0 Ответы
    52 Просмотры
    Последнее сообщение Anonymous
  • Преобразование SQL Reader в SequentialAccess, чтобы избежать ошибок нехватки памяти
    Anonymous » » в форуме C#
    0 Ответы
    34 Просмотры
    Последнее сообщение Anonymous
  • Преобразование SqlDataReader в SequentialAccess, чтобы избежать ошибок нехватки памяти?
    Anonymous » » в форуме C#
    0 Ответы
    32 Просмотры
    Последнее сообщение Anonymous
  • Как избежать сбоя gcc из-за нехватки памяти
    Anonymous » » в форуме C++
    0 Ответы
    25 Просмотры
    Последнее сообщение Anonymous

Вернуться в «C#»