기본 콘텐츠로 건너뛰기

추천 가젯

리액트 + 비트(Vite)로 모바일 청첩장 만들기 — 2편

모바일 청첩장 시리즈 2편 · R2 갤러리 & Firebase 방명록 Cloudflare R2 · Functions Firebase Firestore 클라우드플레어 R2로 갤러리 저장하고, Firebase로 방명록 달기 안녕하세요, 병민입니다 🙌 1편에서 전체 흐름을 잡았고, 이번엔 사진 업로드/보관 과 방명록 을 연결합니다. 서버는 따로 없고 Cloudflare Pages 를 쓰고 있으니, Pages Functions (= 워커)로 R2에 사전서명 URL을 만들어주고, 프론트에서 그 URL로 바로 업로드하는 구조예요. 방명록은 Firebase DB로 간단·안전하게! 전체 그림 프론트(React) → /api/r2/upload 로 업로드용 URL 요청 → R2에 파일 PUT 프론트(React) → /api/r2/list 로 목록 요청 → 갤러리 렌더 프론트(React) → Firebase SDK로 방명록 작성/조회 1) R2 버킷 & Pages Functions 준비 Cloudflare 대시보드 > R2 > Create bucket (예: wedding-gallery ) 버킷 > Settings > CORS 에서 사이트 도메인 허용(예: https://*.pages.dev , 커스텀 도메인) Pages 프로젝트 > Settings > Functions 에서 R2 바인딩 추가: ...

🚀 multiprocessing으로 CPU 병렬 처리 쉽게 구현하기

Multiprocessing icon

🚀 multiprocessing으로 CPU 병렬 처리 쉽게 구현하기

multiprocessing 모듈은 멀티코어 CPU를 활용한 병렬 프로세싱을 가능하게 해 줍니다. 스레드와는 달리 **GIL의 제약 없이** 완전한 병렬 처리를 수행할 수 있어 CPU 바운드 작업에 최적입니다.

✅ 1. `Process`로 직접 프로세스 생성하기

from multiprocessing import Process
import os

def worker(num):
    print(f"프로세스 {num} 시작, PID={os.getpid()}")

if __name__ == '__main__':
    procs = [Process(target=worker, args=(i,)) for i in range(4)]
    for p in procs:
        p.start()
    for p in procs:
        p.join()
  • 각 `Process`는 독립된 메모리 공간을 가지고 실행됩니다.
  • `join()`을 통해 각 프로세스 종료를 기다릴 수 있습니다.

✅ 2. `Pool`을 활용한 작업 분산 처리

from multiprocessing import Pool

def fib(n):
    return n if n < 2 else fib(n-1) + fib(n-2)

if __name__ == '__main__':
    with Pool(processes=4) as pool:
        results = pool.map(fib, [20, 22, 24, 26])
    print(results)
  • `Pool.map()`은 입력 값을 자동으로 분할해 병렬 계산합니다.
  • CPU 병렬화로 재귀 계산이 빠르게 완료됩니다.

✅ 3. `Queue`로 서로 다른 프로세스 간 통신하기

from multiprocessing import Process, Queue

def producer(q):
    for val in ['A','B','C']:
        q.put(val)
    q.put(None)

def consumer(q):
    while True:
        item = q.get()
        if item is None:
            break
        print("소비:", item)

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer, args=(q,))
    p2 = Process(target=consumer, args=(q,))
    p1.start(); p2.start()
    p1.join(); p2.join()
  • 프로세스 간 안전한 데이터 전달, `None`과 같은 종료 시그널 사용 권장.
  • Queue는 내부적으로 립-싱크(lock) 처리를 자동 수행합니다.

✅ 4. `Manager()`로 공유 메모리 값 다루기

from multiprocessing import Manager, Process

def worker(d, l, key, value):
    d[key] = value
    l.append(key)

if __name__ == '__main__':
    mgr = Manager()
    shared_dict = mgr.dict()
    shared_list = mgr.list()
    procs = [Process(target=worker, args=(shared_dict, shared_list, i, i*10)) for i in range(5)]
    for p in procs:
        p.start()
    for p in procs:
        p.join()
    print(shared_list)
    print(shared_dict)
  • 복수 값 공유 및 동시 수정이 필요할 때 유용.
  • 하지만 느릴 수 있으므로 빈번한 접근은 피하세요.

✅ 5. 성능 비교: `map` vs `Pool` vs 단일 함수

import timeit, multiprocessing

setup = """
from multiprocessing import Pool
def heavy(x):
    s = 0
    for i in range(1000000):
        s += i*x
    return s
data = range(4)
"""

t_single = timeit.timeit("list(map(heavy, data))", setup=setup, number=10)
t_pool = timeit.timeit("""
with Pool(processes=4) as p:
    p.map(heavy, data)
""", setup=setup + "\nfrom multiprocessing import Pool", number=10)

print("단일 프로세스:", t_single)
print("Pool 병렬 처리:", t_pool)
  • CPU 바운드 작업 시 `Pool` 사용이 압도적으로 빠름!
  • 작업 단위가 작을 경우 오버헤드가 커질 수 있으니 **적절한 블록 사이즈** 고려하세요.

✨ 요약 & 활용 팁

  • GIL 제약 없이 진정한 병렬 처리 가능 → CPU 바운드 작업에 적합
  • 작업 자동 분배가 필요한 경우 `Pool`이 가장 쉽고 효과적
  • 프로세스 간 통신은 `Queue`, `Manager`로 안전하게 수행할 수 있음
  • 데이터 공유 구조는 편하지만 성능 저하 고려 필요
  • 작업 크기와 프로세스 개수는 벤치마크로 최적화하세요

댓글

가장 많이 본 글

Icons by Flaticon