Racket v9.0의 병렬 스레드

Parallel Threads in Racket v9.0

작성자
HackerNews
발행일
2025년 11월 23일

핵심 요약

  • 1 Racket v9.0은 공유 메모리 병렬 스레드를 도입하여 멀티코어 하드웨어의 성능을 최대한 활용할 수 있도록 지원합니다.
  • 2 새로운 `thread` 함수 인자인 `#:pool 'own`과 `#:keep 'result`를 통해 병렬 스레드 생성 및 결과 관리가 간편해졌습니다.
  • 3 수치 연산 및 비공유 데이터 구조 작업에서 뛰어난 성능 향상을 보이나, I/O 작업에서는 현재 I/O 계층의 잠금으로 인해 확장성이 제한됩니다.

도입

Racket v9.0 출시와 함께 공유 메모리 병렬 스레드에 대한 지원이 추가되었습니다. 이는 이전 버전의 Racket 스레드가 단순히 동시적으로 실행되었던 것과 달리, 멀티코어 하드웨어와 운영체제 스레드를 활용하여 진정한 병렬 실행을 가능하게 합니다. Matthew Flatt, Ryan Culpepper, Robby Findler, Gustavo Massaccesi, Sam Tobin-Hochstadt가 이 중요한 변화를 주도했습니다. 이 글은 Racket의 병렬 처리 도입 배경, 사용법, 성능 특성 및 하위 호환성에 대해 상세히 설명합니다.

Racket v9.0의 병렬 스레드는 개발자가 멀티코어 시스템의 이점을 쉽게 활용할 수 있도록 설계되었습니다. 이는 Racket의 오랜 병렬 처리 여정의 정점입니다.

병렬 스레드 도입

병렬 스레드를 생성하는 것은 thread 호출에 #:pool 'own 플래그를 추가하는 것만큼 간단합니다. 이는 새로운 스레드가 자체 병렬 스레드 풀에서 실행되도록 합니다. 또한, #:keep 'result 인수를 추가하면 스레드 실행 완료 후 결과를 유지할 수 있으며, thread-wait 함수로 결과를 검색할 수 있습니다. 기본적으로 thread 함수는 여전히 코루틴 스레드를 생성하여 하위 호환성을 유지합니다. 코루틴 스레드는 경량이며 선점형으로 스케줄링되어 GUI 상호작용이나 원격 프로세스 오케스트레이션과 같은 동시성 이점이 필요한 작업에 여전히 유용합니다.

Racket의 병렬 처리 여정

Racket은 1990년대 중반에 시작되었으며, 당시에는 멀티프로세서 컴퓨터가 흔치 않아 단일 스레드 런타임의 단순성을 활용했습니다. 이후 placesfutures를 통해 병렬 처리를 지원했지만, 일반적인 병렬 작업에 적용하기에는 특별한 노력이 필요했습니다. 2017년부터 Chez Scheme을 기반으로 Racket을 재구축(Racket CS)하면서 병렬 처리를 위한 더 나은 기반이 마련되었습니다. Chez Scheme의 메모리 일관성 모델 개선과 병렬 가비지 컬렉터 도입은 Racket CS에서 병렬화의 가능성을 크게 높였습니다.

성능 분석

M2 Mac에서의 벤치마크 결과는 병렬 스레드의 성능 이점과 현재 한계를 보여줍니다.

벤치마크 결과 요약

  • 단순 수치 연산 (Fibonacci): 4개 병렬 스레드에서 3.7배, 8개 스레드에서 5.4배의 뛰어난 속도 향상을 보였습니다. futures와 유사한 성능을 나타냈습니다.

  • 문자열 변환 및 할당 (strfib*): 문자열 변환 및 빈번한 할당이 있는 작업에서도 4개 스레드에서 3.7배, 8개 스레드에서 3.9배의 속도 향상을 보였으며, 병렬 가비지 컬렉션의 효과를 확인할 수 있었습니다.

  • 파라미터 의존 작업 (strfib): string->number와 같이 파라미터 값에 의존하는 작업에서 futures는 블록되는 반면, 병렬 스레드는 4개 스레드에서 4배, 8개 스레드에서 4.3배의 속도 향상을 제공하여 더 일관된 이점을 제공했습니다.

  • 해시 테이블 작업 (hash-nums): equal?-기반 해시 테이블 작업에서도 futures는 블록되지만, 병렬 스레드는 4개 스레드에서 3.7배, 8개 스레드에서 4.5배의 성능 향상을 달성했습니다.

병렬 스레드의 한계

  • I/O 작업 (hash-digs, hash-dir): 바이트 스트링 포트에 데이터를 쓰고 해시하는 작업이나 디렉토리 내 파일들을 해시하는 작업에서는 병렬 스레드가 일부 속도 향상을 보였지만, I/O 계층의 굵은 잠금(coarse-grained locks)으로 인해 확장성이 크게 제한되었습니다.

  • 순차 프로그램에 대한 오버헤드: 병렬 스레드 지원을 위한 잠금 메커니즘은 병렬 스레드를 사용하지 않는 순차 프로그램에도 최대 6-8%의 약간의 성능 저하를 초래할 수 있습니다.

하위 호환성 및 고려사항

Racket의 기존 동기화 구조(세마포어, 채널 등)는 병렬 스레드에서도 동일하게 작동하며, box-cas!와 같은 비교-교환(compare-and-swap) 연산도 프로세서 수준의 기본 요소를 사용하므로 안전합니다.

메모리 일관성 모델

병렬 스레드는 기본 머신의 약한 메모리 일관성 모델을 노출할 수 있지만, Racket(정확히는 Chez Scheme)은 메모리 펜스를 사용하여 메모리 안전성을 항상 보장합니다. 이는 기존의 세대별 가비지 컬렉션 및 futures의 메모리 안전성을 지원하는 것과 동일한 메커니즘입니다.

아토믹 모드

병렬 스레드가 아토믹 모드에 진입하면 다른 코루틴 스레드의 실행은 방지하지만, 다른 병렬 스레드의 실행은 방지하지 않습니다. 병렬 스레드에서 아토믹 모드 진입은 코루틴 스레드보다 훨씬 비용이 많이 들 수 있습니다.

외래 함수 호출

코루틴 스레드에서 외래 함수 호출은 사실상 아토믹하지만, 병렬 스레드에서는 그렇지 않습니다. 그러나 대부분의 외래 함수 바인딩은 이미 운영체제 스레드와 호환되도록 설계되었습니다.

결론

Racket v9.0의 병렬 스레드 도입은 Racket의 병렬 처리 능력을 크게 향상시키는 중요한 이정표입니다. 이는 CPU 집약적인 수치 연산 및 비공유 데이터 구조를 다루는 프로그램에서 상당한 성능 이점을 제공합니다. Chez Scheme 기반의 재구축을 통해 마련된 견고한 토대 위에서 구현된 병렬 스레드는 Racket 개발자들에게 멀티코어 하드웨어의 잠재력을 활용할 수 있는 강력하고 직관적인 도구를 제공합니다. 비록 I/O 작업과 같은 일부 영역에서는 아직 개선의 여지가 있지만, 전반적인 성능 향상과 기존 라이브러리와의 높은 호환성은 Racket 생태계에 긍정적인 영향을 미칠 것으로 기대됩니다. Racket은 장기적인 로드맵을 통해 병렬 처리 능력을 지속적으로 강화해 나갈 것입니다.

댓글 0

로그인이 필요합니다

댓글을 작성하거나 대화에 참여하려면 로그인이 필요합니다.

로그인 하러 가기

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!