2016-08-17 8 views
2

저는 Excel 데이터 그리드를 파싱하여 개체 모델을 구축했습니다. 32 개의 열과 100,000 개의 행이 있습니다. 중복 데이터가있는 행을 확인하고이를보고하라는 요청을 받았습니다. 구현을 위해 다음을 수행합니다.C# 수집 성능 : Hashset <string> 및 사전 <string, IList <int>>이이 용도로 가장 빠른 컬렉션입니까?

  1. 작업 사용 행 ID 및 연결된 셀 내용이 포함 된 Tuple 배열을 작성합니다.
  2. 결과 배열을 반복하고 HashSet을 사용하여 연결된 값을 HashSet에 삽입하려고합니다.
  3. HashSet.Add()가 전달되면 내 Dictionary> 결과 집합에 새 항목이 만들어집니다.
  4. HashSet.Add()는 결과가 나머지 프로세스 822s 진행되는 동안 1 단계, 0.09s 소요

설정> 내 내 사전에서 기존 항목에 해당 행 ID를 추가에 실패하면

  • :/누구를 수 컬렉션이나 알고리즘의보다 적절한 선택으로이 시간을 줄일 수있는 곳?

    코드는 다음과 같습니다 :

    var results = new Dictionary<string, IList<int>>(numberOfRows); 
    var hashSet = new HashSet<string>(); 
    var duplicateErrors = new List<string>(); 
    
    for (var row = firstRow; row <= lastRow; row++) 
    { 
        var row1 = row; 
        taskArray[count++] = 
        Task<Tuple<int, string>>.Factory.StartNew(() => GetCompleteRowData(row1, tableRawDataHolders)); 
    } 
    
    foreach (var task in taskArray) 
    { 
        if (hashSet.Add(task.Result.Item2)) 
        { 
         results.Add(task.Result.Item2, new List<int>() { task.Result.Item1 }); 
        } 
        else 
        { 
         results[task.Result.Item2].Add(task.Result.Item1); 
        } 
    } 
    

    public Tuple<int, string> GetCompleteRowData(int row, IEnumerable<ITableRawDataHolder> tableRawDataHolders) 
        { 
         return new Tuple<int, string>(row, string.Join("", 
          tableRawDataHolders.Where(c => c.Row == row).Select(c => c.Value).ToArray())); 
        } 
    

    public class TableRawDataHolder : ITableRawDataHolder 
    { 
        public int Row { get; } 
        public int Column { get; } 
        public string Value { get; } 
    
        public TableRawDataHolder(int row, int column, string value) 
        { 
         Row = row; 
         Column = column; 
         Value = value; 
        } 
    } 
    
  • +0

    실제로'Dictionary' 성능 대 Hashset''에 대해 아무 상관이 테스트 데이터를 생성하는 방법이다. 제목은 비슷하지만 질문을주의 깊게 읽으십시오. OP는 200000 행 사이에서 중복을 찾는다. –

    +1

    * "1 단계는 0.09 초가 걸리고 나머지는 822 초 ​​동안 처리됩니다 : /"*. 실제로는 그렇지 않습니다. 비동기 적으로 작업을 시작하려면 0.09 초가 걸립니다. 그러나,'task.Result'에 액세스하려고하면 스레드를 차단합니다. –

    +0

    @YeldarKurmangaliyev가 좋은 주장을 한 이후로 다시 투표하기로 결정했습니다. 여전히 [이 게시물] (http://stackoverflow.com/q/2728500/993547)이 유용합니다. –

    답변

    2

    이 상황 월호은 사전 또는 HashSet의 성능에 있지 않습니다.

    오버 헤드는 GetCompleteRowData에서 데이터를 읽고 작업을 수행하는 방식에서 비롯됩니다.

    • 다음 번 레코드를 변환해야 할 때마다 전체 컬렉션을 열거하는 것 같습니다.
    • 다음 레코드마다 작은 오버 헤드가 추가되는 작업을 만듭니다. 작업이 종료 될 때까지는 task.Result를 사용할 때까지 대기합니다.
    • 또한 얼마나 빨리 ITableRawDataHolder가 데이터를 반환하는지 분명하지 않습니다.

    순수한 해시/사전 성능을 증명하기 위해 이미 준비된 튜플의 배열을 반복 테스트하는 테스트를 만들었습니다. 이 코드는 내 컴퓨터 (i7 쿼드 코어)에서 32ms 밖에 걸리지 않습니다. 여기

    const Int32 numberOfRows = 200000; 
    var inputData = GetInputData(numberOfRows); 
    var results = new Dictionary<string, IList<int>>(numberOfRows); 
    var hashSet = new HashSet<string>(); 
    
    var sw = Stopwatch.StartNew(); 
    foreach (var dataItem in inputData) 
    { 
        if (hashSet.Add(dataItem.Item2)) 
        { 
         results.Add(dataItem.Item2, new List<int>() {dataItem.Item1}); 
        } 
        else 
        { 
         results[dataItem.Item2].Add(dataItem.Item1); 
        } 
    } 
    Console.WriteLine(sw.ElapsedMilliseconds); 
    

    내가 (이 일부 실제 중복 포함)

    private static List<Tuple<int, String>> GetInputData (int numberOfRows) 
    { 
        var result = new List<Tuple<int, String>>(numberOfRows); 
        var rnd = new Random(); 
        for (var i = 0; i < numberOfRows; i++) 
        { 
         // Once in 100 records we'll have not unique value 
         if (result.Count > 0 && rnd.Next(100)%1 == 0) 
         { 
          result.Add(new Tuple<int, string>(i, result[rnd.Next(result.Count)].Item2)); 
         } 
         else 
          result.Add(new Tuple<int, string>(i, Guid.NewGuid().ToString())); 
        } 
        return result; 
    } 
    
    +0

    예를 들어 주셔서 감사합니다. 내 입력을 변경하고 IDIctionary > 키를 행 번호로 감싼다는 생각이 들었다. 더 이상 모든 행의 모든 ​​데이터를 통해 LINQ를 쿼리 할 필요가 없으며 처리 시간이 822 초에서 22 초로 단축되었습니다. 이 문제에 정말로 도움을 주셔서 감사합니다. –

    +0

    도움이 된 것을 기쁘게 생각합니다. 22 초는 여전히 꽤 많이 보입니다. Excel에서 데이터를 가져 왔다고 말했 으면 Excel에서 데이터의 양을 최적화하여 읽을 수 있습니다. 예를 들어 셀별로 셀을 읽는 대신 전체 셀 범위를 한 번에 배열로 읽는 것이 더 빠른 경우도 있습니다. – dlxeon

    +0

    전체 시트는 32 열 × 100000 행입니다. 추가 병목 현상을 찾아내는 코드를 살펴 보겠습니다. –