본문 바로가기
Algorithm

SIMD와 MIMD 차이점 쉽게 설명

by markbyun 2025. 4. 23.

컴퓨팅 성능을 개선하는 중요한 요소들 중 하나가 병렬 처리입니다. 그리고 이런 병렬 컴퓨팅을 지원하는 중요한 두 가지 모델은 SIMD (Single Instruction, Multiple Data)와 MIMD (Multiple Instruction, Multiple Data)입니다. 이러한 모델을 이해하면 알고리즘을 최적화하고 하드웨어 성능을 효과적으로 활용하는데 도움이 됩니다. 이 블로그에서는 이 두 모델들에 대해서 간략히 설명하고, 어떻게 파이썬 내장 함수에서 활용되고 있는지 그리고 어떻게 파이썬으로 구현 가능한지 살펴보겠습니다.


SIMD: Single Instruction, Multiple Data

개념

SIMD는 하나의 연산이 여러 데이터 포인트에 동시에 적용되는 병렬 컴퓨팅 모델입니다. 이미지 처리, 신호 처리, 선형 대수 연산과 같은 데이터 병렬 작업에 매우 효율적입니다.

예시

SIMD 예시: 벡터 덧셈

    A = [1, 2, 3, 4]
    B = [5, 6, 7, 8]
    C = A + B (요소별 덧셈)

프로세서 SIMD 유닛:
    1단계: ADD 1+5 | 2+6 | 3+7 | 4+8 => [6, 8, 10, 12]

모든 연산은 전문화된 벡터 레지스터 (예: x86 CPU의 SSE, AVX)를 사용하여 동기화된 방식으로 실행됩니다.

Python에서의 SIMD 지원

Python은 NumPy 및 Numba와 같은 라이브러리를 통해 SIMD를 활용할 수 있습니다.

NumPy (암시적 SIMD)

import numpy as np  # 수치 연산을 위한 NumPy 가져오기

# 두 개의 1D NumPy 정수 배열 생성
A = np.array([1, 2, 3, 4], dtype=np.int32)
B = np.array([5, 6, 7, 8], dtype=np.int32)

# 요소별 덧셈 수행; NumPy는 내부적으로 최적화된 C 수준 SIMD 루틴을 사용함
C = A + B

print(C)  # 출력: [ 6  8 10 12]

이 코드는 NumPy가 내부적으로 SIMD를 사용하여 벡터화된 연산을 수행하는 방법을 보여줍니다. + 연산자는 요소별 덧셈을 위해 오버로딩되어 있습니다.

Numba (JIT을 통한 명시적 SIMD)

from numba import vectorize, int32  # Numba에서 vectorize 데코레이터 및 int32 타입 가져오기

# 두 정수를 더하는 벡터화된 함수 정의
@vectorize([int32(int32, int32)], target='parallel')  # target='parallel'은 SIMD를 활성화함
def add(a, b):
    return a + b

# 입력 배열 생성
A = np.array([1, 2, 3, 4], dtype=np.int32)
B = np.array([5, 6, 7, 8], dtype=np.int32)

# 벡터화된 함수 호출
C = add(A, B)

print(C)  # 출력: [ 6  8 10 12]

Numba의 @vectorize 데코레이터는 add 함수를 SIMD가 활성화된 루프로 컴파일합니다. 이를 통해 Python 방식으로 고성능 저수준 벡터화 연산을 작성할 수 있습니다.


MIMD: Multiple Instruction, Multiple Data

개념

MIMD 아키텍처는 여러 프로세서가 서로 다른 명령어를 서로 다른 데이터에 대해 독립적으로 실행할 수 있도록 합니다. 이는 멀티코어 CPU, 클러스터, 분산 시스템에서 일반적입니다.

예시

MIMD 예시: 독립 작업

    코어 1: process_image(image1)
    코어 2: process_image(image2)
    코어 3: save_results(result1)
    코어 4: log_statistics(stats)

각 코어는 고유한 명령어 스트림을 실행합니다.

Python에서의 MIMD 지원

Python은 multiprocessing 및 concurrent.futures를 사용하여 MIMD를 지원합니다.

Multiprocessing

from multiprocessing import Process  # multiprocessing 모듈에서 Process 클래스 가져오기

# 별도의 프로세스에서 실행할 함수 정의
def task(name):
    print(f"Processing {name}")  # 작업 처리 시뮬레이션

# Process 객체 생성
p1 = Process(target=task, args=("Image1",))
p2 = Process(target=task, args=("Image2",))

# 두 프로세스 시작
p1.start()
p2.start()

# 두 프로세스가 완료될 때까지 대기
p1.join()
p2.join()

이 코드는 두 개의 별도 프로세스를 생성하여 병렬로 실행합니다. 각각의 프로세스는 task 함수를 독립적으로 실행하여 진정한 MIMD 실행을 보여줍니다.

Concurrent Futures

from concurrent.futures import ProcessPoolExecutor  # 프로세스 기반 병렬 처리를 위한 ProcessPoolExecutor 가져오기

# 숫자의 제곱을 계산하는 함수 정의
def compute(x):
    return x * x

# 컨텍스트 매니저를 사용하여 프로세스 풀 관리
with ProcessPoolExecutor() as executor:
    results = list(executor.map(compute, [1, 2, 3, 4]))  # 여러 프로세스에 작업 분산
    print(results)  # 출력: [1, 4, 9, 16]

ProcessPoolExecutor는 워커 프로세스 풀을 사용하여 병렬 실행을 간단하게 만듭니다. executor.map 메서드는 값 목록에 대해 compute 함수를 병렬로 수행합니다.


참고 문헌

  1. Hennessy, J. L., & Patterson, D. A. (2019). Computer Architecture: A Quantitative Approach. Morgan Kaufmann.
  2. Numba 공식 문서 - https://numba.pydata.org/
  3. Python multiprocessing - https://docs.python.org/3/library/multiprocessing.html
  4. Python concurrent.futures - https://docs.python.org/3/library/concurrent.futures.html
  5. NumPy SIMD 최적화 - https://numpy.org/doc/stable/user/whatisnumpy.html