C# 시니어 코딩·알고리즘 기술면접
새 면접Q. 수백만 건의 주문 데이터를 처리하는 시스템에서 여러 스레드가 동시에 재고를 차감하는 상황입니다. Lock을 최소화하면서 재고 정합성을 보장하는 알고리즘 접근법을 설명하고, 각 방법의 시간 복잡도와 trade-off를 분석해주세요.
Lock-Free 자료구조와 낙관적 동시성 제어 방식을 고려해보세요.
Interlocked 클래스를 활용한 CAS(Compare-And-Swap) 연산으로 Lock-Free 방식을 구현할 수 있습니다. Interlocked.CompareExchange를 사용하여 재고 값을 원자적으로 업데이트하며, 실패 시 재시도하는 스핀락 방식으로 구현합니다. 시간 복잡도는 경합이 적을 때 O(1)이지만, 경합이 심할 경우 재시도로 인해 성능이 저하될 수 있습니다. 대안으로 ConcurrentDictionary와 같은 .NET의 동시성 컬렉션을 활용하거나, 재고를 샤딩하여 경합을 분산시키는 방법도 있습니다. 극단적인 고성능이 필요한 경우 Actor 모델(예: Akka.NET)을 도입하여 재고별로 단일 스레드 처리를 보장하는 아키텍처도 고려할 수 있습니다.
- • Interlocked.CompareExchange를 활용한 Lock-Free CAS 연산
- • 경합 수준에 따른 시간 복잡도 변화 이해 (O(1) ~ 재시도 오버헤드)
- • 샤딩, Actor 모델 등 대안적 접근법과 trade-off 분석
이커머스 플랫폼의 플래시 세일이나 티켓팅 시스템에서 동시 주문 처리 시 재고 정합성을 보장해야 할 때 사용됩니다.
재고 차감 실패율이 30%를 넘어가는 상황이라면 어떤 아키텍처 변경을 제안하시겠습니까?
Q. 10GB 크기의 로그 파일에서 상위 100개의 빈출 IP 주소를 찾아야 합니다. 메모리는 2GB로 제한되어 있습니다. 이 문제를 해결하기 위한 알고리즘 접근법을 설명하고, 시간 및 공간 복잡도를 분석해주세요.
External Sort와 분할 정복 방식, 그리고 효율적인 카운팅 자료구조를 함께 고려해보세요.
Hash Partitioning과 Min-Heap을 조합한 접근법을 사용합니다. 먼저 파일을 스트리밍으로 읽으면서 IP 주소를 해시 함수로 N개의 파티션 파일로 분할합니다(O(n) 시간). 각 파티션은 메모리에 적재 가능한 크기가 되도록 N을 조정합니다. 각 파티션별로 Dictionary를 사용해 IP별 카운트를 집계하고(O(m) 공간, m은 파티션 내 고유 IP 수), 크기 100인 Min-Heap을 유지하며 상위 100개를 추출합니다(O(m log 100)). 모든 파티션의 상위 100개를 병합하여 최종 상위 100개를 선택합니다(O(N*100 log 100)). 전체 시간 복잡도는 O(n)이며, 공간 복잡도는 O(m + 100)으로 메모리 제약을 만족합니다. 대안으로 Count-Min Sketch 같은 확률적 자료구조를 사용하면 메모리를 더 절약할 수 있지만 정확도가 약간 감소합니다.
- • Hash Partitioning을 통한 대용량 데이터 분할 처리
- • Min-Heap을 활용한 Top-K 알고리즘 적용
- • 스트리밍 처리로 메모리 제약 극복 및 공간 복잡도 O(m) 유지
대규모 웹 서버 로그 분석, DDoS 공격 탐지, 트래픽 패턴 분석 등에서 제한된 메모리로 대용량 데이터를 처리할 때 사용됩니다.
IP 주소 분포가 매우 skewed되어 특정 파티션이 메모리를 초과한다면 어떻게 처리하시겠습니까?
Q. LRU 캐시를 C#으로 구현할 때 Get과 Put 연산을 모두 O(1) 시간 복잡도로 만들기 위한 자료구조 설계와 구현 원칙을 설명해주세요. 또한 멀티스레드 환경에서 안전하게 만들기 위한 고려사항도 함께 설명해주세요.
빠른 조회와 순서 유지를 동시에 제공하는 두 가지 자료구조의 조합을 생각해보세요.
Dictionary와 Doubly Linked List를 조합하여 구현합니다. Dictionary는 키를 LinkedListNode의 참조로 매핑하여 O(1) 조회를 제공하고, Doubly Linked List는 접근 순서를 유지하며 O(1) 삽입/삭제를 보장합니다. Get 연산 시 Dictionary에서 노드를 찾아 리스트의 헤드로 이동시키고, Put 연산 시 새 노드를 헤드에 추가하며 용량 초과 시 테일 노드를 제거합니다. 멀티스레드 환경에서는 ReaderWriterLockSlim을 사용하여 읽기는 동시 허용, 쓰기는 배타적으로 제어하거나, ConcurrentDictionary를 사용하되 리스트 조작 시에만 lock을 사용하는 하이브리드 접근도 가능합니다. 대안으로 .NET의 MemoryCache를 활용하거나, 높은 동시성이 필요하면 lock-free 알고리즘 기반의 써드파티 라이브러리를 검토할 수 있습니다.
- • Dictionary와 Doubly Linked List 조합으로 O(1) Get/Put 구현
- • ReaderWriterLockSlim을 활용한 읽기-쓰기 동시성 최적화
- • MemoryCache 등 .NET 기본 제공 대안과의 trade-off 이해
API 응답 캐싱, 데이터베이스 쿼리 결과 캐싱, 세션 관리 등 고성능이 요구되는 웹 애플리케이션에서 널리 사용됩니다.
캐시 히트율이 낮아서 LRU보다 LFU(Least Frequently Used)가 더 적합한 상황이라면 자료구조를 어떻게 변경하시겠습니까?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!