본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] CPU Affinity 설정을 통한 멀티프로세싱 성능 극대화 방법과 2가지 해결책

by Papa Martino V 2026. 2. 26.
728x90

CPU Affinity 설정
CPU Affinity 설정

 

파이썬의 multiprocessing 모듈을 사용하여 병렬 처리를 구현할 때, 대부분의 개발자는 운영체제(OS)의 스케줄러가 알아서 최적의 코어에 프로세스를 할당할 것이라고 믿습니다. 하지만 연산 집약적인(CPU-bound) 작업을 수행할 때, OS 스케줄러가 프로세스를 여러 코어로 빈번하게 이동시키는 Context Switching(컨텍스트 스위칭) 현상은 캐시 적중률을 떨어뜨리고 성능 저하를 유발하는 주범이 됩니다. 이를 방지하기 위한 기술이 바로 CPU Affinity(CPU 선호도) 설정입니다. 오늘 이 글에서는 특정 프로세스를 특정 물리 코어에 고정하여 성능을 극대화하는 전문적인 기법을 상세히 다룹니다.


1. CPU Affinity의 개념과 일반 병렬 처리와의 차이점

CPU Affinity란 프로세스나 스레드가 실행될 CPU 코어를 명시적으로 지정하는 기능입니다. 파이썬에서는 os.sched_setaffinity (리눅스) 또는 psutil 라이브러리를 통해 이를 제어할 수 있습니다. 프로세스를 특정 코어에 바인딩하면 CPU 캐시(L1, L2)의 데이터를 재사용할 확률이 높아져 연산 속도가 눈에 띄게 향상됩니다.

기본 멀티프로세싱과 Affinity 최적화 모델 비교

비교 항목 표준 Multiprocessing CPU Affinity 최적화 성능 향상 포인트
코어 할당 방식 OS 스케줄러가 임의 결정 사용자가 특정 코어에 고정 불필요한 프로세스 이동 방지
L1/L2 캐시 효율 코어 이동 시 캐시 미스 발생 캐시 데이터 유지(Warm Cache) 메모리 접근 레이턴시 감소
컨텍스트 스위칭 빈번하게 발생 가능 최소화됨 오버헤드 15~30% 감소
작업 예측 가능성 유동적임 매우 높음 실시간성(Real-time) 보장 유리

2. 성능 저하를 유발하는 2가지 스케줄링 간섭 문제

첫째, 프로세스 바운싱(Process Bouncing)

운영체제는 시스템 전체의 로드 밸런싱을 위해 실행 중인 프로세스를 다른 코어로 옮기는 경향이 있습니다. 이 과정에서 현재 코어의 캐시에 쌓인 데이터가 무효화되는데, 대규모 행렬 연산이나 딥러닝 추론 작업 시 이는 치명적인 지연을 초래합니다.

둘째, 하이퍼스레딩(Hyper-threading)의 함정

논리 코어(Logical Core)는 물리 코어의 자원을 공유합니다. 두 개의 프로세스를 같은 물리 코어 내의 서로 다른 논리 코어에 할당하면 자원 경합이 발생하여 성능이 오히려 떨어질 수 있습니다. 진정한 성능 극대화를 위해서는 물리 코어당 하나의 프로세스만 할당하는 정밀한 계산이 필요합니다.


3. [Sample Example] psutil을 활용한 코어 바인딩 해결 코드

다음은 파이썬에서 psutil 모듈을 사용하여 자식 프로세스가 생성될 때 각각 고유한 물리 코어 번호를 부여받도록 강제하는 전문적인 구현 예제입니다.


import multiprocessing
import os
import psutil
import time

def heavy_computation(core_index):
    """지정된 코어에서만 실행되는 고부하 연산 함수"""
    # 1. 현재 프로세스 객체 가져오기
    p = psutil.Process(os.getpid())
    
    # 2. CPU Affinity 설정 (특정 코어 인덱스에 고정)
    try:
        p.cpu_affinity([core_index])
        print(f"[Process {os.getpid()}] 고정된 CPU 코어: {core_index}")
    except Exception as e:
        print(f"Affinity 설정 실패: {e}")

    # 3. 실제 연산 수행 (예: 단순 루프)
    start_time = time.time()
    count = 0
    for _ in range(10**7):
        count += 1
    
    print(f"[Core {core_index}] 연산 완료 시간: {time.time() - start_time:.4f}초")

if __name__ == "__main__":
    # 시스템의 물리 코어 개수 확인
    num_cores = psutil.cpu_count(logical=False)
    print(f"시스템 물리 코어 수: {num_cores}")

    processes = []
    for i in range(num_cores):
        # 각 프로세스에 서로 다른 코어 인덱스 전달
        p = multiprocessing.Process(target=heavy_computation, args=(i,))
        processes.append(p)
        p.start()

    for p in processes:
        p.join()

    print("모든 병렬 작업이 최적화된 상태로 종료되었습니다.")

4. CPU Affinity 극대화를 위한 3단계 운영 전략

  1. Isolate Cores (OS 레벨): 리눅스의 경우 isolcpus 커널 파라미터를 사용하여 특정 코어를 OS 스케줄러 대상에서 제외한 후, 파이썬 프로세스만 해당 코어를 점유하게 하면 간섭을 0에 가깝게 줄일 수 있습니다.
  2. NUMA(Non-Uniform Memory Access) 고려: 멀티 소켓 CPU 서버를 사용한다면, 프로세스가 데이터를 처리하는 메모리와 해당 CPU 코어가 물리적으로 가까운 위치에 있는지 확인하여 메모리 대역폭 손실을 방지하십시오.
  3. 물리 코어 우선순위 할당: 논리 코어 번호 0, 1번이 같은 물리 코어인 경우가 많으므로, 짝수 번호(0, 2, 4...) 코어에만 프로세스를 할당하는 식으로 하이퍼스레딩 간섭을 회피하십시오.

5. 결론 및 요약

파이썬 멀티프로세싱 성능을 한 단계 더 끌어올리기 위해서는 운영체제의 관리에만 의존하지 말고 하드웨어 자원에 대한 직접적인 제어권을 가져와야 합니다. CPU Affinity 설정을 통해 컨텍스트 스위칭을 억제하고 캐시 효율을 극대화하는 기법은 고성능 컴퓨팅(HPC) 및 실시간 데이터 처리 시스템 구축의 필수 과제입니다. 본문에 제시된 해결 방법을 통해 여러분의 어플리케이션에 최적화된 병렬 구조를 완성해 보시기 바랍니다.

핵심 요약 프로세스를 특정 CPU 코어에 고정하여 캐시 효율 증대 및 오버헤드 감소
해결 방법 psutil 라이브러리를 활용한 물리 코어별 독립적 Affinity 바인딩 구현

내용 출처 및 참고 문헌

  • Python Documentation: os.sched_setaffinity and multiprocessing strategies
  • Psutil Documentation: Process Class - cpu_affinity method
  • High Performance Python: CPU-Bound Task Optimization by Ian Ozsvald (2025 Revised)
  • Intel 64 and IA-32 Architectures Software Developer's Manual: Cache and Memory Control
728x90