Словарь C# и эффективное использование памяти

У меня есть инструмент для сравнения 2 файлов CSV и затем блока каждая ячейка в один из этих 6 блоков. В основном это читает в файлах CSV (использующий быстрого csv читателя, кредит: http://www.codeproject.com/KB/database/CsvReader.aspx), и затем создает словарь, имеющий отношение к каждому файлу на основе ключей, обеспеченных пользователем. Я тогда выполняю итерации через th словари, сравнивающие значения и пишущие файл CSV результата.

В то время как это сверкает быстро, это очень неэффективно с точки зрения использования памяти. Я не могу сравнить файлы больше чем на 150 МБ на своем поле с 3 ГБ физической памяти.

Вот фрагмент кода для чтения ожидаемого файла. В конце этой части использование памяти близко к 500 МБ от диспетчера задач.

// Read Expected
long rowNumExp;
System.IO.StreamReader readerStreamExp = new System.IO.StreamReader(@expFile);
SortedDictionary dictExp = new SortedDictionary();
List listDupExp = new List();
using (CsvReader readerCSVExp = new CsvReader(readerStreamExp, hasHeaders, 4096))
{
    readerCSVExp.SkipEmptyLines = false;
    readerCSVExp.DefaultParseErrorAction = ParseErrorAction.ThrowException;
    readerCSVExp.MissingFieldAction = MissingFieldAction.ParseError;
    fieldCountExp = readerCSVExp.FieldCount;                
    string keyExp;
    string[] rowExp = null;
    while (readerCSVExp.ReadNextRecord())
    {
        if (hasHeaders == true)
        {
            rowNumExp = readerCSVExp.CurrentRecordIndex + 2;
        }
        else
        {
            rowNumExp = readerCSVExp.CurrentRecordIndex + 1;
        }
        try
        {
            rowExp = new string[fieldCount + 1];                    
        }
        catch (Exception exExpOutOfMemory)
        {
            MessageBox.Show(exExpOutOfMemory.Message);
            Environment.Exit(1);
        }                
        keyExp = readerCSVExp[keyColumns[0] - 1];
        for (int i = 1; i < keyColumns.Length; i++)
        {
            keyExp = keyExp + "|" + readerCSVExp[i - 1];
        }
        try
        {
            readerCSVExp.CopyCurrentRecordTo(rowExp);
        }
        catch (Exception exExpCSVOutOfMemory)
        {
            MessageBox.Show(exExpCSVOutOfMemory.Message);
            Environment.Exit(1);
        }
        try
        {
            rowExp[fieldCount] = rowNumExp.ToString();
        }
        catch (Exception exExpRowNumOutOfMemory)
        {
            MessageBox.Show(exExpRowNumOutOfMemory.Message);
            Environment.Exit(1);
        }
        // Dedup Expected                        
        if (!(dictExp.ContainsKey(keyExp)))
        {
            dictExp.Add(keyExp, rowExp);                        
        }
        else
        {
            listDupExp.Add(rowExp);
        }                    
    }                
    logFile.WriteLine("Done Reading Expected File at " + DateTime.Now);
    Console.WriteLine("Done Reading Expected File at " + DateTime.Now + "\r\n");
    logFile.WriteLine("Done Creating Expected Dictionary at " + DateTime.Now);
    logFile.WriteLine("Done Identifying Expected Duplicates at " + DateTime.Now + "\r\n");                
}

Есть ли что-нибудь, я мог сделать для создания этого большей памятью эффективный? Что-нибудь я мог сделать по-другому выше, для потребления меньшего количества mermory?

Любые идеи приветствуются.

Спасибо парни для всей обратной связи.

Я включил изменения, как предложено сохранить индекс строки вместо самой строки в словарях.

Вот тот же фрагмент кода с новой реализацией.

// Read Expected
        long rowNumExp;
        SortedDictionary dictExp = new SortedDictionary();
        System.Text.StringBuilder keyExp = new System.Text.StringBuilder();
        while (readerCSVExp.ReadNextRecord())
        {
            if (hasHeaders == true)
            {
                rowNumExp = readerCSVExp.CurrentRecordIndex + 2;
            }
            else
            {
                rowNumExp = readerCSVExp.CurrentRecordIndex + 1;
            }
            for (int i = 0; i < keyColumns.Length - 1; i++)
            {
                keyExp.Append(readerCSVExp[keyColumns[i] - 1]);
                keyExp.Append("|");
            }
            keyExp.Append(readerCSVExp[keyColumns[keyColumns.Length - 1] - 1]);
            // Dedup Expected                       
            if (!(dictExp.ContainsKey(keyExp.ToString())))
            {
                dictExp.Add(keyExp.ToString(), rowNumExp);
            }
            else
            {
                // Process Expected Duplicates          
                string dupExp;
                for (int i = 0; i < fieldCount; i++)
                {
                    if (i >= fieldCountExp)
                    {
                        dupExp = null;
                    }
                    else
                    {
                        dupExp = readerCSVExp[i];
                    }
                    foreach (int keyColumn in keyColumns)
                    {
                        if (i == keyColumn - 1)
                        {
                            resultCell = "duplicateEXP: '" + dupExp + "'";
                            resultCell = CreateCSVField(resultCell);
                            resultsFile.Write(resultCell);
                            comSumCol = comSumCol + 1;
                            countDuplicateExp = countDuplicateExp + 1;
                        }
                        else
                        {
                            if (checkPTColumns(i + 1, passthroughColumns) == false)
                            {
                                resultCell = "'" + dupExp + "'";
                                resultCell = CreateCSVField(resultCell);
                                resultsFile.Write(resultCell);
                                countDuplicateExp = countDuplicateExp + 1;
                            }
                            else
                            {
                                resultCell = "PASSTHROUGH duplicateEXP: '" + dupExp + "'";
                                resultCell = CreateCSVField(resultCell);
                                resultsFile.Write(resultCell);
                            }
                            comSumCol = comSumCol + 1;
                        }
                    }
                    if (comSumCol <= fieldCount)
                    {
                        resultsFile.Write(csComma);
                    }
                }
                if (comSumCol == fieldCount + 1)
                {
                    resultsFile.Write(csComma + rowNumExp);
                    comSumCol = comSumCol + 1;
                }
                if (comSumCol == fieldCount + 2)
                {
                    resultsFile.Write(csComma);
                    comSumCol = comSumCol + 1;
                }
                if (comSumCol > fieldCount + 2)
                {
                    comSumRow = comSumRow + 1;
                    resultsFile.Write(csCrLf);
                    comSumCol = 1;
                }
            }
            keyExp.Clear();
        }
        logFile.WriteLine("Done Reading Expected File at " + DateTime.Now + "\r\n");
        Console.WriteLine("Done Reading Expected File at " + DateTime.Now + "\r\n");
        logFile.WriteLine("Done Analyzing Expected Duplicates at " + DateTime.Now + "\r\n");
        Console.WriteLine("Done Analyzing Expected Duplicates at " + DateTime.Now + "\r\n");
        logFile.Flush();

Однако проблема, мне нужны оба наборы данных в памяти. Я на самом деле выполняю итерации и через словари, ища соответствия, несоответствия, дубликаты и через уволенных на основе ключа.

Используя этот подход хранения индекса строки, я все еще использую большую память, потому что для динамического доступа я должен теперь использовать кэшированную версию csv читателя. Таким образом, хотя словарь является значительно уменьшенным теперь, кэширование данных восполняет сбережения, и я все еще закончил приблизительно с подобным использованием памяти.

Надежда, я имею смысл...:)

Одна опция состоит в том, чтобы избавиться от словаря полностью и просто цикла через эти 2 файла, но не уверенная, если производительность с такой скоростью, как сравнила бы 2 словаря.

Любые исходные данные очень ценятся.

9
задан user262102 30 January 2010 в 00:53
поделиться

3 ответа

-- 2517964-

Если вы еще не получили профилировщик на этом, как Dottrace, чтобы увидеть, как объекты, использующие память, которые даст вам хорошее представление о том, что нужно оптимизировать.

Некоторые идеи посмотреть код:

Вам нужно хранить listdupexp? Мне кажется в списке, вы эффективно загружаете оба файла в память, так что 2 × 150 МБ + некоторые накладные расходы могут легко подойти 500 МБ в диспетчере задач.

Во-вторых, вы можете начать запись вывода, прежде чем прочитать все вход? Я предполагаю, что это сложно, как выглядит так, будто вам нужны все выходные элементы, отсортированные, прежде чем писать их, но, возможно, что вы могли бы посмотреть.

2
ответ дан 4 December 2019 в 19:33
поделиться

Скажите, если я получу что-нибудь не так.

Код выше считывает один файл CSV и ищет дублирующиеся клавиши. Каждая строка входит в один из двух наборов, для дублирующихся клавиш, и один без.

Что вы делаете с этими врезанами?

Пишены ли они в разные файлы?

Если так, что нет никаких причин хранить неразрешенные строки в списке, как вы найдете их, пишите их в файл.

Когда вы найдуте дубликаты, нет необходимости хранить всю строку, просто храните ключ и напишите строку в файл (очевидно, другой файл, если вы хотите сохранить их отдельные).

Если вам нужно сделать дальнейшую обработку на различных наборах, а затем вместо хранения всей строки, если не хранить номер строки. Тогда, когда вы делаете то, что это за когда-либо, вы делаете со строками, у вас есть нумерация строки, чтобы снова получить ряд.

NB: вместо того, чтобы сохранить номер строки, вы можете хранить смещение в файле пункт начальной точки строки. Затем вы можете получить доступ к файлу и прочитать строки случайным образом, если вам нужно.

Просто прокомментируйте этот ответ с любыми вопросами (или разъяснениями), которые вы могли бы обновить ответ, я буду здесь еще пару часов.

Редактировать
Вы можете уменьшить печать ног памяти, не сохраняя клавиши, а сохранение хэшей клавиш. Если вы найдете дубликат, обратитесь к этой позиции в файле, перечитайте строку и сравните фактические клавиши.

3
ответ дан 4 December 2019 в 19:33
поделиться

Вы можете заменить keyExp с помощью StringBuilder. перераспределение строки в таком цикле будет продолжать выделять больше памяти, поскольку строки неизменяемы.

StringBuilder keyExp = new StringBuilder();
...
    keyExp.Append("|" + readerCSVExp[i - 1]) ;
... 

многие ли струны одинаковы? вы можете попробовать интернировать их , тогда любые идентичные строки будут использовать одну и ту же память, а не будут копиями ...

rowExp[fieldCount] = String.Intern(rowNumExp.ToString()); 

// Dedup Expected               
string internedKey = (String.Intern(keyExp.ToString()));        
if (!(dictExp.ContainsKey(internedKey)))
{
   dictExp.Add(internedKey, rowExp);                        
}
else
{
   listDupExp.Add(rowExp);
}  

Я не уверен, как именно работает код, но ... кроме этого, я бы скажем, вам не нужно хранить rowExp в словаре, сохраните что-нибудь еще, например число, и запишите rowExp обратно на диск в другом файле. Это, вероятно, сэкономит вам больше всего памяти, поскольку это кажется массивом строк из файла, поэтому, вероятно, он большой. Если вы записываете его в файл и сохраняете номер в файле, то вы можете вернуться к нему снова в будущем, если вам нужно будет обработать. Если вы сохранили смещение в файле как значение в словаре, вы сможете быстро найти его снова. Может быть :).

7
ответ дан 4 December 2019 в 19:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: