bfiber_1700
Boost.Fiber Tuning
Tuning
📦 Disable synchronization - 동기화 비활성화
컴파일러의 명령줄에 'BOOST_FIBERS_NO_ATOMICS'를 정의하면 (다른 스레드에 있는) 파이버 간의 동기화가 비활성화됩니다. 이는 애플리케이션이 단일 스레드이거나 파이버가 스레드 간에 동기화되지 않는 경우 허용됩니다.
📦 Memory allocation - 메모리 할당
메모리 할당 알고리즘은 멀티스레드 환경, 특히 파이버 스택이 힙에 할당되는 Boost.Fiber의 성능에 중요합니다. 'glibc'의 기본 사용자 수준 메모리 할당자(UMA:user-level memory allocator)는 'ptmalloc2'이지만 구체적인 작업 부하에 더 적합한 다른 UMA로 대체될 수 있습니다. 예를 들어 Google의 'TCmalloc'은 'glibc'의 기본 메모리 할당자보다 'skynet microbenchmark'에서 더 나은 성능을 제공합니다.
📦 Scheduling strategies - 일정 전략
스레드의 파이버는 파이버 관리자에 의해 조정됩니다. 파이버는 선제적으로(preemptively) 통제하기보다는 협력적으로(cooperatively) 통제권을 거래합니다. 작업 부하(work-load)에 따라 알고리즘을 대신하여 구현될 수 있는 파이버 스케줄링의 여러 전략이 가능합니다[10].
- 작업 훔치기(work-stealing): 준비된 파이버가 로컬 큐에 보관되어 파이버 스케줄러의 로컬 큐에 준비된 파이버가 부족하면 무작위로 다른 파이버 스케줄러를 선택하고 피해자에게서 준비된 파이버를 훔치려고 합니다(work_stealing 및 numa::work_stealing 구현됨)
- 작업 요청(work-requesting): 파이버 스케줄러의 로컬 큐에 준비된 파이버가 부족할 때, 준비된 파이버는 로컬 큐에 보관됩니다. 다른 파이버 스케줄러를 무작위로 선택하고 준비된 파이버를 요청합니다. 피해자(victim) 파이버 스케줄러는 준비된 파이버를 다시 보냅니다.
- 작업 공유(work-sharing): 준비된 파이버는 글로벌 큐에 보관되며, 파이버 스케줄러는 동시에 글로벌 큐로/에서 준비된 파이버를 푸시 및 팝합니다(shared_work에서 구현됨).
- 작업 분산(work-distribution): 준비된 파이버는 유휴 파이버 스케줄러 또는 부하가 낮은 파이버 스케줄러에 사전에 분산됩니다.
- 작업 균형 조정(work-balancing): 전용(도우미) 파이버 스케줄러는 다른 스레드에서 실행 중인 모든 파이버 스케줄러에 대한 정보를 주기적으로 수집하고 준비된 파이버를 그들 사이에 재분배합니다.
📦 TTAS(test-test-and-set) locks
Boost.Fiber는 내부적으로 스핀록을 사용하여 서로 다른 스레드에서 실행되는 파이버가 상호 작용하는 경우 임계 영역을 보호합니다.
Spinlock은 TTAS(test-test-and-set) 잠금으로 구현됩니다.
즉, 'spinlock'은 원자 교환을 호출하기 전에 잠금을 테스트합니다.
이 전략은 잠금 획득(acquiring)/해제(releasing)에 의해 발생하는 캐시 라인(cache line) 무효화를 줄이는 데 도움이 됩니다.
⚛ TTAS lock 설명 (chatgpt)
📦 Spin-wait loop
다른 스레드가 더 빠르기 때문에 스레드가 잠금 획득에 반복적으로 실패하는 경우 잠금은 경합 중인 것으로 간주됩니다. 짧은 시간 동안 기다리면 다른 스레드가 임계 섹션에 다시 들어가기 전에 완료됩니다. 잠금을 기다리는 동안 CPU를 완화(relaxing)하면(일시 중지(pause)/양보(yield) 니모닉을 통해) 코드가 회전 대기(spin-wait) 루프에 있다는 힌트를 CPU에 제공합니다.
- 비용이 많이 드는 파이프라인 플러시를 방지합니다 (추측적으로(speculatively) 실행된 로드 및 비교 명령이 파이프라인에 푸시되지 않음).
- 다른 하드웨어 스레드(동시 멀티스레딩)가 타임 슬라이스를 얻을 수 있습니다.
- 몇 가지 CPU 주기를 지연시키지만 기아 상태(starvation)를 방지하는 데 필요합니다.
스레드가 다른 스레드를 실행하기 위해 시간 분할을 포기하는 경우에만 잠금이 해제될 수 있기 때문에 이 전략은 단일 코어 시스템에서는 쓸모가 없다는 것이 분명합니다.
매크로 'BOOST_FIBERS_SPIN_SINGLE_CORE'는 스레드가 해당 타임 슬라이스를 포기하고 운영 체제가 다른 스레드로 전환한다는 사실을 운영 체제에 (std::this_thread_yield()를 통해) 알려 CPU 힌트(일시 중지(pause)/양보(yield) 니모닉)를 대체합니다.
📦 Exponential back-off - 재시도 시간 간격 조정
BOOST_FIBERS_RETRY_THRESHOLD 매크로는 스레드를 생성하거나 futex-wait를 차단하기 전에 CPU가 스핀 대기(spin-wait) 루프에서 반복하는 횟수를 결정합니다.
스핀록(spinlock)은 스레드가 잠금 획득에 실패한 횟수를 추적합니다.
경합이 높을수록 스레드가 백오프되는 시간이 길어집니다.
이 목적을 위해 무작위 경합 창과 함께 "이진 지수 백오프(Binary Exponential Backoff)" 알고리즘이 활용됩니다.
'BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD'는 경합 창의 상한을 결정합니다(2를 기준으로 지수로 표시).
⚛ Exponential back-off 설명 (chatgpt)
📦 Speculative execution (hardware transactional memory) - 추측 실행(HTM)
Boost.Fiber는 스핀록(spinlocks)을 사용하여 트랜잭션 메모리와 함께 사용할 수 있는 임계 영역을 보호합니다(추측 실행 섹션 참조).
• Note
htm=tsx 속성이 b2 명령줄에 지정되고 'BOOST_USE_TSX'가 컴파일러에 적용되면 'TXS'가 활성화됩니다.
• Note
부동 소수점 상태가 중요 영역 내에서 수정되면 TSX 트랜잭션이 중단됩니다.
결과적으로 부동 소수점 연산, 예: 파이버(컨텍스트) 전환 중에 부동 소수점 관련 레지스터의 찢어짐/로드가 비활성화됩니다.
📦 NUMA systems
최신 다중 소켓 시스템은 일반적으로 NUMA 시스템으로 설계됩니다. 'numa::work_stealing'과 같은 적합한 파이버 스케줄러는 원격 메모리 액세스(대기 시간/latence)를 줄입니다.
📦 Parameters
표 1.5. 컴파일 명령줄에서 정의할 수 있는 매개변수
Parameter | Default value | Effect on Boost.Fiber |
---|---|---|
BOOST_FIBERS_NO_ATOMICS | - | no multithreading support, all atomics removed, no synchronization between fibers running in different threads |
BOOST_FIBERS_SPINLOCK_STD_MUTEX | - | std::mutex used inside spinlock |
BOOST_FIBERS_SPINLOCK_TTAS | + | spinlock with test-test-and-swap on shared variable |
BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE | - | spinlock with test-test-and-swap on shared variable, adaptive retries while busy waiting |
BOOST_FIBERS_SPINLOCK_TTAS_FUTEX | - | spinlock with test-test-and-swap on shared variable, suspend on futex after certain number of retries |
BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_FUTEX | - | spinlock with test-test-and-swap on shared variable, while busy waiting adaptive retries, suspend on futex certain amount of retries |
BOOST_FIBERS_SPINLOCK_TTAS + BOOST_USE_TSX | - | spinlock with test-test-and-swap and speculative execution (Intel TSX required) |
BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE + BOOST_USE_TSX | - | spinlock with test-test-and-swap on shared variable, adaptive retries while busy waiting and speculative execution (Intel TSX required) |
BOOST_FIBERS_SPINLOCK_TTAS_FUTEX + BOOST_USE_TSX | - | spinlock with test-test-and-swap on shared variable, suspend on futex after certain number of retries and speculative execution (Intel TSX required) |
BOOST_FIBERS_SPINLOCK_TTAS_ADAPTIVE_FUTEX + BOOST_USE_TSX | - | spinlock with test-test-and-swap on shared variable, while busy waiting adaptive retries, suspend on futex certain amount of retries and speculative execution (Intel TSX required) |
BOOST_FIBERS_SPIN_SINGLE_CORE | - | on single core machines with multiple threads, yield thread (std::this_thread::yield()) after collisions |
BOOST_FIBERS_RETRY_THRESHOLD | 64 | max number of retries while busy spinning, the use fallback |
BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD | 16 | max size of collisions window, expressed as exponent for the basis of two |
BOOST_FIBERS_SPIN_BEFORE_SLEEP0 | 32 | max number of retries that relax the processor before the thread sleeps for 0s |
BOOST_FIBERS_SPIN_BEFORE_YIELD | 64 | max number of retries where the thread sleeps for 0s before yield thread (std::this_thread::yield()) |
⚛ 원문