1. 멀티 프로세싱 시작하기 (feat. GIL)

jj·2021년 5월 26일
0
post-thumbnail

다음은 <파이썬 동시성 프로그래밍> 을 읽고 발췌 및 정리한 내용입니다.

참고: https://bowbowbow.tistory.com/16

이 블로그의 포스팅은 굉장히 잘 설명되어있었다!

Thread

사무실에 한 직원이 여러 일을 하고 있다. 메일 업무, 프로그래밍, 프로젝트 매니징 등등

이 직원은 똑똑해서 메일 업무-> 프로그래밍 -> 프로젝트 매니징의 순서로 각 업무를 켠왕하는게 아니라, 메일업무 조금 했다가, 프로그래밍도 조금 하고, 프로젝트 매니징도 조금하는 식으로 일을 효율적으로 처리한다.

이렇게, 한명의 작업자가 시간을 쪼개어 여러 작업을 진행하는 멀티태스킹을 멀티 스레딩이라고 한다. 그리고 작업자는 프로세스가 된다. (하나의 프로세스 안에 여러개의 스레드가 존재하는 형식)

만약 회사에서 더 많은 일을 병렬적으로 하고 싶다면 더 많은 직원을 뽑아야한다. 파이썬에서는 프로세스가 더 필요하다.

스레딩의 장점은 다음과 같다:

  • 다수의 스레드는 I/O bound가 막혔을 때, 속도를 획기적으로 높여준다.
    • I/O bound: 연산은 간단한데 입출력이 많을 때 속도가 떨어지는 현상
  • 프로세서와 비교했을 때, 메모리를 조금 차지한다.
  • 스레드는 자원(과 메모리)을 공유하기 때문에, 스레드 간의 통신이 쉽다

스레딩의 단점 도 물론 존재한다:

  • CPython의 스레드는 GIL로 인해 사용의 제약이 따른다
  • 스레드 간의 통신이 쉬워진 반면, race condition이 발생하지 않도록 주의해야한다.
  • 다수의 스레드 간에 context를 바꾸는 데 수많은 계산이 필요하다. 다수의 스레드를 추가함으로써 전반적인 프로그램의 성능이 저하된다.

Process

프로세스는 스레드가 할 수 있는 거의 모든 것이 가능하다. 중요한 것은 단일 CPU 코어에 국한되지 않는다는 것이다.

4코어 CPU라면, 사무실에서 직원을 3명 더 고용하는 것이라고 생각하면 된다. 4명 중 두명은 프로그래밍, 2명은 프로젝트 매니징을 한다고 생각하면 된다.

프로세스는 하나의 주 스레드를 갖고 있지만, 각 스레드마다 자체의 register와 stack을 포함한 다수의 서브 스레드를 만들 수 있다. 주의할 것은, 프로세스는 자원을 공유하지 않기 때문에, 프로세스 간 통신에서 시간이 낭비될 수 있다.

왼쪽의 프로세스는 하나의 주 스레드만 가지고 있고, 오른쪽 프로세스는 여러 스레드를 가지고 있으며, 각 스레드마다 register와 stack으로 구성되어있다.

프로세스의 장점 은 다음과 같다:

  • 멀티 코어 프로세서를 사용해 프로세스의 성능을 높일 수 있다
  • CPU 가 많이 필요한 작업(많은 연산량)의 경우 멀티스레드보다 유용하다
  • 멀티프로세스를 이용해 GIL의 한계를 피할 수 있다 (이후에 더 자세하게 살펴보자!_!)
  • 프로세스의 충돌은 전체 프로그램에 영향을 주지 않는다

프로세스의 단점 도 있다:

  • 프로세스간 의 공유 자원이 없어서 IPC 형태로 구현해야한다
    • 그러나, 공유 상태가 많이 않다는 것은 곧, 코드 내부에서, race condition을 고려하지 않아도 된다는 소리가 된다.
  • 많은 메모리가 필요하다

프로세스의 구성요소

  • Process ID, process group ID, user ID, group ID
  • 환경
  • 작업 디렉토리
  • program instruction
  • register
  • stack
  • heap
  • File discriptor(파일 기술자): 운영체제에서 파일을 사용할 때 각 파일에 대한 정보를 유지하는 기억 장치의 한 영역 및 정보
  • 신호 동작
  • 공유 라이브러리
  • 프로세스간 통신도구(메시지 큐, 파이프, 세마포어, 공유 메모리)

멀티 프로세싱

우리들의 컴퓨터는 생각보다 많은 CPU를 가지고 있다. 살 때만 스펙을 신경쓰고, 오래 되면 까먹어서 우리의 컴퓨터를 얕보지 말자.

내 노트북은 현재 8개의 코어를 가지고 있다.

하나의 코어만 사용하도록 제어하면, 나머지 코어는 쉽게 유휴상태가 되는데, 우리는 이 쉬고 있는 녀석들을 뽑아먹어야 한다.

파이썬의 한계

GIL, 그놈은 누구인가?

GIL이란 병렬 파이썬 코드를 실행할 때, 여러 스레드의 사용을 제한하는 Mutual exclusion lock이다. 말이 어려운데, 해석을 해보면, 여러명이 (상호)이 접근하는 것을 제한(exclusion)하는 잠금장치(lock)이다 (여러개의 병렬 프로세스가 공통의 변수 및 자원에 액세스할 때 임의의 시점에서 하나의 프로세스만 액세스 하도록 제어하는 것)

한번에 1개의 스레드만 유지하는 잠금장치(lock)이며, 스레드를 자체코드에서 실행하려면 자체 코드를 실행하기 전에 lock 을 먼저 점유해야한다. 이로 인해 lock이 실행되는 동안에는 모든 실행이 불가능하다

이 그림을 보면, 각 스레드는 다음 작업을 진행하기 전에 GIL을 기다리고 받아야 하며, 작업을 완료하기 전에 GIL을 해제해야한다. 이는 무작위 라운드 로빈 방식을 사용하기 때문에, 어떤 스레드가 lock을 먼저 점유할 지 알 수 없다.
이 게임을 떠올리면 이해가 쉽다. 일단 무작위 가위바위보 게임에서 이겨서 뿅망치를 점유해야지 때릴 수 있다.

GIL을 제거하려는 노력이 있었지만, 안정적인 스레드를 보장하는 lock의 추가가 2배 이상의 성능감소로 이어졌다. 2개 이상의 CPU보다 1개의 CPU를 통한 작업이 더 불리하다는 것이다. 그러나 GIL을 사용하지 않는 numpy 같은 라이브러리도 있다

Further Questions

스레드는 할 수 있고, 프로세스는 못하는 일이 무엇일까?

profile
재밌는게 재밌는거다

0개의 댓글