2010-01-29 30 views
9

Tôi có một công cụ để so sánh 2 tệp csv và sau đó đưa từng ô vào một trong 6 nhóm. Về cơ bản, nó đọc trong các tập tin csv (sử dụng nhanh csv đọc, tín dụng: http://www.codeproject.com/KB/database/CsvReader.aspx) và sau đó tạo ra một từ điển liên quan đến mỗi tập tin dựa trên các phím được cung cấp bởi người sử dụng. Sau đó tôi lặp qua các từ điển thứ hai so sánh các giá trị và viết một tệp csv kết quả.C# Từ điển và Sử dụng Bộ nhớ Hiệu quả

Trong khi nó rất nhanh, việc sử dụng bộ nhớ rất kém hiệu quả. Tôi không thể so sánh hơn 150 MB tệp trên hộp của tôi với bộ nhớ vật lý 3 GB.

Đây là đoạn mã để đọc tệp mong muốn. Ở cuối đoạn này, việc sử dụng bộ nhớ là gần 500 MB từ trình quản lý tác vụ.

// 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");     
} 

Tôi có thể làm gì để làm cho bộ nhớ hiệu quả hơn không? Bất cứ điều gì tôi có thể làm khác đi ở trên, để tiêu thụ ít mermory?

Mọi ý tưởng đều được chào đón.

Cảm ơn mọi người vì tất cả các phản hồi.

Tôi đã kết hợp các thay đổi như được đề xuất để lưu trữ chỉ mục của hàng thay vì chính hàng trong từ điển.

Đây là đoạn mã giống với triển khai mới.

// 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(); 

Tuy nhiên, vấn đề là tôi cần cả bộ dữ liệu trong bộ nhớ. Tôi thực sự lặp qua cả hai từ điển tìm kiếm các kết quả phù hợp, không phù hợp, trùng lặp và bỏ học dựa trên khóa.

Sử dụng phương pháp này để lưu trữ chỉ mục hàng, tôi vẫn đang sử dụng rất nhiều bộ nhớ vì truy cập động tôi phải sử dụng phiên bản được lưu trong bộ nhớ cache của trình đọc csv. Vì vậy, mặc dù từ điển nhỏ hơn nhiều bây giờ, bộ nhớ đệm của dữ liệu bù đắp cho các khoản tiết kiệm và tôi vẫn kết thúc với việc sử dụng bộ nhớ tương tự.

Hope, tôi đang làm cho cảm giác ... :)

Một lựa chọn là để thoát khỏi từ điển hoàn toàn và chỉ cần vòng qua 2 tác phẩm, nhưng không chắc chắn nếu thực hiện sẽ nhanh như so sánh 2 từ điển.

Mọi yếu tố đầu vào đều được đánh giá cao.

+0

thay vì lưu bộ nhớ cache của trình đọc csv, bạn không thể lưu vào bộ nhớ cache vị trí bản ghi trong tệp, để bạn có thể lấy lại các bản ghi sau? khi bạn lặp lại thông qua các từ điển tìm kiếm bỏ học vv bởi chính là bạn nhìn vào dữ liệu thực tế hoặc chỉ là các phím? –

+0

bạn có cố gắng thực hiện chuỗi trước khi chúng đi vào từ điển không? nó có sự khác biệt không? Có bất kỳ điều này đã giúp với việc sử dụng bộ nhớ ở tất cả? –

Trả lời

7

Bạn có thể thay thế keyExp bởi một StringBuilder. phân bổ lại chuỗi trong một vòng lặp như vậy sẽ tiếp tục cấp phát bộ nhớ nhiều hơn vì các chuỗi không thay đổi được.

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

có nhiều chuỗi giống nhau không? bạn có thể thử interning them, sau đó bất kỳ chuỗi giống hệt nhau sẽ chia sẻ bộ nhớ tương tự chứ không phải là bản sao ...

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); 
} 

Tôi không chắc chắn chính xác cách thức hoạt động mã nhưng ... ngoài mà tôi muốn nói rằng bạn don không cần giữ rowExp trong từ điển, giữ cái gì khác, như số và viết rowExp sao lưu vào đĩa trong tệp khác. Điều này có lẽ sẽ giúp bạn tiết kiệm bộ nhớ nhiều nhất vì điều này dường như là một chuỗi các chuỗi từ tệp nên có lẽ là lớn. Nếu bạn viết nó vào một tập tin và giữ số trong tập tin của nó tại thì bạn có thể quay trở lại với nó một lần nữa trong tương lai nếu bạn cần xử lý.Nếu bạn đã lưu độ lệch trong tệp dưới dạng giá trị trong từ điển, bạn có thể tìm lại nó một cách nhanh chóng. Có lẽ :).

+0

Thú vị, tôi đã nghĩ rằng trình biên dịch/thông dịch/jitter/một cái gì đó interned chuỗi tự động, nhưng đó có lẽ chỉ cho stings được biết là giống hệt nhau tại thời gian biên dịch tôi đoán. – Davy8

+0

@ Davy8, đó là chính xác. Việc thực thi chuỗi chỉ xảy ra theo mặc định trên các chuỗi được tạo từ các hằng số biên dịch thời gian. –

3

Hãy cho tôi biết nếu tôi gặp bất kỳ điều gì sai.

Đoạn mã trên đọc một tệp CSV và tìm các khóa trùng lặp. Mỗi hàng đi vào một trong hai bộ, các hàng cho các khóa trùng lặp và một không có.

Bạn làm gì với những hàng này?

Chúng có được ghi vào các tệp khác nhau không?

Nếu vậy, không có lý do gì để lưu trữ các hàng không bất thường trong danh sách, vì bạn thấy chúng ghi chúng vào một tệp.

Khi bạn tìm thấy bản sao, không cần phải lưu toàn bộ hàng, chỉ lưu trữ khóa và ghi hàng vào tệp (rõ ràng là một tệp khác nếu bạn muốn giữ chúng riêng biệt).

Nếu bạn cần xử lý thêm trên các bộ khác nhau, thì thay vì lưu toàn bộ hàng, khi không lưu trữ số hàng. Sau đó, khi bạn làm những gì bao giờ nó là bạn làm với các hàng, bạn có số hàng necessarry để lấy hàng một lần nữa.

NB: thay vì lưu trữ số hàng, bạn có thể lưu trữ bù trừ trong tệp của điểm bắt đầu của hàng. Sau đó, bạn có thể truy cập tệp và đọc các hàng ngẫu nhiên, nếu bạn cần.

Chỉ cần nhận xét câu trả lời này với bất kỳ câu hỏi (hoặc làm rõ) bạn có thể có, tôi sẽ cập nhật câu trả lời, tôi sẽ ở đây cho một vài giờ anyway.

Chỉnh sửa
Bạn có thể giảm bớt chân bộ nhớ thêm bằng cách không lưu khóa, nhưng lưu giữ băm khóa. Nếu bạn tìm thấy một bản sao, hãy tìm đến vị trí đó trong tệp, đọc lại hàng và so sánh các khóa thực tế.

+0

Vui lòng xem trả lời của tôi trong bài đăng đã chỉnh sửa ở trên. Rất tiếc, không biết cách dán thành công mẫu mã trong nhận xét. – user262102

2

Nếu bạn chưa có kiến ​​thức về điều này giống như DotTrace để xem các đối tượng đang sử dụng bộ nhớ, điều đó sẽ cho bạn ý tưởng tốt về những gì cần tối ưu hóa.

Một số ý tưởng khi xem mã:

Bạn có cần lưu trữ danh sáchDupExp không? Dường như với tôi với danh sách bạn đang có hiệu quả tải cả hai tập tin vào bộ nhớ để 2 x 150MB + một số chi phí có thể dễ dàng tiếp cận 500MB trong công việc quản lý.

Thứ hai, bạn có thể bắt đầu viết đầu ra trước khi bạn đọc tất cả các đầu vào không? Tôi đoán đây là điều khó khăn vì có vẻ như bạn cần tất cả các mục đầu ra được sắp xếp trước khi bạn viết chúng ra, nhưng có thể là thứ bạn có thể xem.

Các vấn đề liên quan