Restartable Sequences 이해하기: Mutex와 Atomics를 대체하는 고성능 대안

극한의 성능을 추구하는 과정에서 개발자들은 종종 동기화 문제를 다룰 때 한계에 부딪힙니다. Mutex와 atomic operations 같은 전통적인 도구들은 여러 스레드 간의 데이터 무결성을 보장하는 표준이지만, 오버헤드라는 비용이 따릅니다. 코어 수가 증가함에 따라 이러한 잠금(lock)에 대한 경합이 심각한 병목 현상을 일으킬 수 있으며, 이는 CPU 사이클 낭비와 지연 시간 증가로 이어집니다.

Restartable sequences (rseq)는 Linux 커널이 임계 구역(critical section)을 처리하는 방식의 패러다임 전환을 나타냅니다. 무거운 잠금 메커니즘에 의존하는 대신, rseq는 프로그램이 짧은 명령 시퀀스를 실행할 수 있게 하며, 만약 선점(preemption)이 발생할 경우 커널이 이를 안전하게 중단하고 재시작할 수 있도록 하여, 사용자 공간 메모리 업데이트에 대해 효과적으로 "트랜잭션" 방식의 접근을 제공합니다.

Restartable Sequences의 작동 방식

rseq의 핵심은 임계 구역 실행 중 발생하는 선점 문제를 처리하도록 설계되었다는 점입니다. 일반적인 시나리오에서 스레드가 잠금을 보유한 상태로 선점되면, 다른 스레드들은 spin하거나 block되어 자원을 낭비하게 됩니다. Restartable sequences를 사용하면 커널은 스레드가 진입하는 임계 구역을 인지할 수 있습니다.

커뮤니티 논의에서 언급된 바와 같이, 메커니즘은 다음과 같이 작동합니다:

"작동 방식은 프로그램이 중단되기를 원하지 않는 코드의 임계 구역에 진입할 때마다 커널에 알리는 것입니다... 첫 번째 어셈블리 opcode는 rseq_cs 필드를 설정하는 move instruction이어야 합니다. 마지막 명령은 전역 데이터 구조에 수정을 가하는 작업이어야 합니다."

이 프로세스는 아주 작은 사용자 공간 트랜잭션을 생성합니다. 사용자 공간 애플리케이션과 커널 사이의 통신은 양방향이며 공유 메모리를 통해 이루어지므로, 임계 구역 동안 비용이 큰 시스템 호출(system call)을 할 필요가 없습니다. 만약 커널이 스레드를 선점하거나 스레드가 다른 CPU로 마이그레이션되면, 커널은 rseq_cs 필드가 설정되었음을 감지하고 명령 포인터를 시퀀스의 시작 부분으로 자동 재설정하여, 작업이 스레드의 실행과 관련하여 원자적으로 재시작되고 완료되도록 보장합니다.

성능상의 이점: Atomics를 넘어서

rseq에 관한 가장 흥미로운 주장 중 하나는 CPU의 내부 atomic operations보다도 더 높은 성능을 낼 수 있다는 것입니다. Atomics는 mutexes보다 빠르지만, 여전히 코어 간의 캐시 라인 동기화(cache line synchronization)를 필요로 하며, 이는 고동시성 환경에서 비용이 많이 들 수 있습니다.

rseq를 사용하면 개발자는 특정 핫 패스(hot paths)에서 mutexes와 atomics를 모두 제거할 수 있습니다. 커널이 재시작 로직을 관리하기 때문에 애플리케이션은 단순한 load와 store를 수행할 수 있습니다. 선점이 발생하지 않는다면 코드는 로우 메모리 액세스(raw memory access) 속도로 실행됩니다. 만약 선점이 발생한다면, 그 비용은 매우 짧은 명령 시퀀스(통상 10개 이하)를 재시작하는 것이며, 이는 경합이 발생하는 잠금의 오버헤드보다 훨씬 효율적입니다.

실무적인 구현 및 도구

기저 메커니즘은 어셈블리와 공유 메모리 구조를 포함하지만, 대부분의 개발자에게 rseq를 처음부터 구현하는 것은 필요하지 않습니다. 생태계는 더 높은 수준의 추상화 기능을 제공하도록 발전해 왔습니다.

이 기능을 프로젝트에 통합하려는 사람들을 위해, librseq 라이브러리(rseq 구현자가 유지 관리함)는 카운터나 연결 리스트와 같은 일반적인 사용 사례에 필요한 필수 헬퍼 함수들을 제공합니다. 이를 통해 개발자들은 모든 임계 구역에 대해 커스텀 어셈블리를 작성할 필요 없이 restartable sequences의 성능 이점을 누만할 수 있습니다.

고려 사항 및 트레이드오프

성능 향상에도 불구하고, rseq는 만능 해결책이 아닙니다. rseq는 매우 짧은 임계 구역에 최적화되어 있습니다. 시퀀스가 너무 길면 선점 확률이 높아져 빈번한한 재시작이 발생하고 잠재적으로 성능이 저하될 수 있습니다.

또한, 일부 개발자들은 rseq의 이점이 대규모 코어 수를 가진 환경에서 가장 두드러진다고 주장합니다. 애플리케이션 개발자가 소수의 스레드를 완전히 제어할 수 있는 시렌리오에서는 rseq와 전통적인 thread-local storage나 최적화된 atomics 사이의 격차가 좁을 수 있습니다. 하지만 하드웨어가 수백 개의 코어로 계속 확장됨에 따라, 단순히 스레드가 아닌 CPU 코어와 함께 확장할 수 있는 능력은 결정적인 이점이 됩니다.

결론

Restartable sequences는 "원자성"의 책임을 하드웨어(atomics)나 OS 스케줄러(mutexes)에서 두 요소 사이의 협력 관계(cooperative relationship)로 전환함으로써 동기화 오버헤드를 줄이는 정교한 방법을 제공합니다. 임계 구역을 공유 메모리를 통한 재시작 가능한 트랜잭션으로 취급함으로써, Linux는 고성능 컴퓨팅에서 진정한 선형적 확장을 가능하게 하는 경로를 제공합니다.

Sources