Dictinary와 ConcurrentDictionary의 성능 비교(C#)

NightMiya827·2023년 12월 17일
0

(블로그 이사하면서 옮겨진 글입니다)

  • 이 블로그 작성에 사용된 코드는 github에 올라가있습니다.

C#에는 멀티쓰레드 프로그래밍을 도와주는 ConcurrentQueue, ConcurrentDictionary같은 자료구조들이 있다. 그래서 기본적으로는 싱글쓰레드에서만 접근할 때는 Dictionary, 멀티쓰레드에서 접근할때는 ConcurrentDictionary를 쓰는데, 문득 'Dictionary를 안써도 되는게 아닐까?'라는 생각이 들었다. ConcurrentDictionary가 Dictionary보다 느린 건 당연하지만 느린 정도가 프로그램에 영향을 끼치지 않는 수준이라면 모든 Dictionary를 ConcurrentDictionary로 교체할 수 있을 것이다. 그래서 두개의 성능을 비교해보기로 했다.


실험방법

  • ConcurrentDictionary는 concurrencyLevel=1로 설정해서 사용
  • <int,int> 타입으로 총 1억개의 데이터를 넣었다 빼면서 실험
    • 최대 사이즈가 1억
  • TryAdd, Remove, Values에 대해서 시간 측정
  • 총 소모 시간과 각 시도의 소모시간을 기록
  • 총 소모 시간은 100번을 반복하여 그 평균치를 기록
  • 시작 사이즈(capacity)는 1

실험 결과

Add

ConcurrentDictionaryDictionary
Total334.3ms30.3ms
Max124.7ms34.4ms
  • 약 11배 차이가 남
  • Dictionary에 1억개를 넣는 평균 시간이 30.3ms인데, 1개를 넣는데 34.4ms가 걸린 케이스가 존재한다.
    • 개인 컴퓨터에서 돌렸고, Max치는 한번만 측정하는거라서 값이 얼마든지 튈 수 있다. 참고용으로 추가했지만 사실 무의미한 값에 가깝다.

Remove

ConcurrentDictionaryDictionary
Total34.6ms8.8ms
  • 약 4배 차이

Iteration

ConcurrentDictionaryDictionary
Total4.6ms2.9ms
  • 약 1.5배 차이

결론

구체적인 수치는 실험실환경이랑은 많이 동떨어진 개인용 윈도우 컴퓨터에서 측정되었기 때문에 많은 오차가 있을 수 있다. 하지만 ConcurrentDictionary에는 무시할 수 없을만큼의 성능차이가 존재한다는 사실은 확인할 수 있다.

또한 Add에서는 특별히 시간차이가 많이 난다. 그래서 capacity가 1이기 때문에 Resize에서 시간차이가 많이 나기 때문이라고 생각해서 Capacity를 1억으로 설정하고 비교해본 결과

ConcurrentDictionaryDictionary
Total129.9ms17.0ms
Max13.5ms0.1ms

여전히 많은 차이가 났다.

capacity를 늘려도 key 충돌에 의해 resize로직이 돌 수 있는게 문제인 듯 했다. 한 item을 add하는데 1ms이상 걸리는 경우가 concurrentDictionary에 한해서 몇번 발생하고 이게 키 충돌에의한 resize가 아닐까 추측된다. (comparer만들고 getHashCode 구현하면 이것도 없앨 수 있을 것 같지만 거기까지 하긴 귀찮았다) 왜 concurrentDictionary에서만 이런 resize과정이 일어나는지에 대한 자세한 내용은 ConcurrentDictionary 코드Dictionary 코드를 직접 확인해보자.

결론적으로, 성능차이가 많이나기 때문에 성능에 어느정도 민감한 프로그램에서는 Dictionary 대신 ConcurrentDictionary를 쓰진 못할 듯 하다. 하지만 Dictionary가 5배 이상 느려도 별 문제가 없어보인다면 추가 프로파일링 과정을 거친 후에 도입을 고민해볼 수는 있을 것 같다.

0개의 댓글

관련 채용 정보