簡體   English   中英

C#字典和高效的內存使用

[英]C# Dictionary and Efficient Memory Usage

我有一個工具來比較2個csv文件,然后將每個單元格裝入6個桶中的一個。 基本上,它讀取csv文件(使用快速csv閱讀器,信用: http//www.codeproject.com/KB/database/CsvReader.aspx ),然后根據提供的密鑰創建一個與每個文件有關的字典。用戶。 然后我遍歷比較值並寫入結果csv文件的字典。

雖然速度非常快,但在內存使用方面效率非常低。 我無法比較我的盒子上超過150 MB的文件和3 GB的物理內存。

這是一個用於讀取預期文件的代碼段。 在這篇文章的最后,任務管理器的內存使用量接近500 MB。

// Read Expected
long rowNumExp;
System.IO.StreamReader readerStreamExp = new System.IO.StreamReader(@expFile);
SortedDictionary<string, string[]> dictExp = new SortedDictionary<string, string[]>();
List<string[]> listDupExp = new List<string[]>();
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<string, long> dictExp = new SortedDictionary<string, long>();
        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個字典一樣快。

任何投入都非常感謝。

您可以用StringBuilder替換keyExp 在這樣的循環中重新分配字符串將繼續分配更多的內存,因為字符串是不可變的。

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寫回另一個文件中的磁盤。 這可能會節省你最多的內存,因為這似乎是文件中的一個字符串數組,所以可能很大。 如果您將其寫入文件並將其保存在文件中,那么如果您需要處理,將來可以再次返回該文件。 如果您將文件中的偏移量保存為字典中的值,則可以快速找到它。 也許 :)。

如果我弄錯了,請告訴我。

上面的代碼讀取一個CSV文件並查找重復的密鑰。 每行進入兩組中的一組,一組用於重復鍵,一組用於沒有。

你如何處理這些行集?

他們寫的是不同的文件嗎?

如果是這樣,沒有理由將非unqiue行存儲在列表中,因為您發現它們將它們寫入文件。

當你找到重復項時,不需要存儲整行,只需存儲密鑰,並將行寫入文件(如果你想讓它們保持獨立,顯然是一個不同的文件)。

如果需要對不同的集合進行進一步處理,則不存儲整行,而不是存儲行號。 然后,當你對行進行任何操作時,你需要行號才能再次獲取行。

注意:您可以將偏移量存儲在行的起始點的文件中,而不是存儲行號。 然后,如果需要,您可以隨機訪問該文件並讀取行。

只要對你可能有的任何問題(或澄清)評論這個答案,我會更新答案,無論如何我會在這里待幾個小時。

編輯
您可以通過不存儲鍵來進一步減少內存占用,而是存儲鍵的哈希值。 如果您發現重復,請在文件中尋找該位置,重新讀取該行並比較實際的鍵。

如果您還沒有像DotTrace那樣獲得一個探查器來查看哪些對象正在使用內存,那么這將讓您對需要優化的內容有所了解。

查看代碼的一些想法:

你需要存儲listDupExp嗎? 在我看來,列表中你有效地將兩個文件加載到內存中,因此2 x 150MB +一些開銷在任務管理器中很容易接近500MB。

其次,你可以在閱讀所有輸入之前開始編寫輸出嗎? 我認為這很棘手,因為看起來你需要在寫出之前對所有輸出項進行排序,但可能是你可以看到的東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM