CS Insights

동시성의 극한 속도: 거대한 큐 병목을 부순 LMAX Disruptor 링 버퍼 매커니즘

동시성의 극한 속도: 거대한 큐 병목을 부순 LMAX Disruptor 링 버퍼 매커니즘
초고빈도 거래(HFT)를 다루는 가상 자산 오더북(Orderbook) 엔진을 자바로 개발하면서, BlockingQueue에 매수/매도 주문을 담아 스레드끼리 전달하는 구조를 짰더니 락(Lock) 경합만으로 처리 지연율이 밀리초를 우습게 돌파했습니다. 우리는 코드를 버리고 하드웨어 CPU의 레이아웃을 공부하기 시작했습니다. 그 방향의 끝에 영국의 LMAX 거래소가 고안해 낸 괴물 같은 프레임워크인 Disruptor가 있었습니다. 일반적인 큐(Queue)는 머리와 꼬리에 포인터를 가지고 있으며, 데이터를 넣고 뺄 때마다 멀티 스레드가 뮤텍스 락을 걸고 해제합니다. 더 끔찍한 것은 메모리 상에 객체가 파편화되어 할당되므로 CPU L1 캐시 히트율이 바닥을 친다는 것입니다. Disruptor는 큐를 가차 없이 버리고, 메모리에 한 덩어리로 붙어있는 거대한 원형 배열(Ring Buffer)을 미리 찍어내어 할당해 냅니다. 여기에 시퀀스 배리어(Sequence Barrier)라는 무식한 일련번호 체계 하나만을 둡니다. 생산자(Publisher) 스레드들은 배열의 자리를 선점하기 위해 락을 걸지 않고, CPU가 하드웨어 레벨에서 지원하는 CAS(Compare-And-Swap) 원자적 명령어 하나로 자신의 시퀀스 번호를 가로채 배열 칸을 확보합니다. 소비자는 그저 이전 시퀀스 번호의 작업이 끝났는지만 Spin-Wait(루프 대기)로 감시하며 미친 듯이 배열을 질주합니다. 게다가 캐시 라인(64바이트)이 CPU 코어 간에 겹치면서 무의미하게 강제 동기화되는 거짓 공유(False Sharing) 현상조차 막기 위해 양옆에 여백(Padding)을 8바이트씩 채워놓는 변태적인 최적성까지 발휘합니다. 자바의 JVM을 쓰고 있음에도 소프트웨어가 컴파일러를 넘어 실리콘 메모리 버스와 공감대(Mechanical Sympathy)를 맞추었을 때 터져 나오는 초당 수백만 트랜잭션의 락 프리(Lock-Free) 마법은 엔지니어링의 최고 예술이었습니다.

Related Posts