파비의 매일매일 공부기록

파이썬 동시성 프로그래밍 - #1 시작하기 본문

Study/Python

파이썬 동시성 프로그래밍 - #1 시작하기

fabichoi 2021. 8. 8. 23:30

백엔드 실무를 하다 보면 생각보다 중요한 부분이 '동시성'에 대한 내용이다.

사용자 요청 중에는 바로바로 반영할 필요가 없으면서 데이터의 양이 많은 시나리오가 꽤 있다. 

이런 경우 Task 등을 통해서 비동기로 처리를 할 수 있는데, 이때 동시성에 대한 내용을 고려해야 한다.

그리고 개인적으로 좀 궁금하기도 한 분야 중 하나다.

다중 프로세서에서 일을 한 번에 시키는 건, 그만큼 효율을 증가할 수 있는 거니 꽤나 고급 프로그래밍 중 하나일 것 같다는 생각이 들기도 한다.

 

어쨌든 첫 장은 동시성에 대한 기본적인 내용을 소개한다.

 

예전에 공부할 때 세마포어(semaphore)라는 용어를 봤었는데, 이게 수기 신호라는 뜻이라고 하고

원래는 기차 등에서 사용하는 '까치발 신호기'라고 한다. (아래 링크의 그림 참조)

https://upload.wikimedia.org/wikipedia/commons/e/e9/Korail_Yeongdong_Line_Jeongdongjin_Station_Semaphore.jpg

이처럼 동시성의 개념은 철도와 전신 분야에서 유래되었다고 한다. 이건 아예 처음 알게 된 사실.

 

그리고 인물로는 알고리즘 문제풀이 때 마지막 관문 같은 '다익스트라 최단 경로 알고리즘'의 창시자(?) 다익스트라가 유명하다. 이 형은 정말 컴싸에 길이길이 남을 위인인 듯..

 

스레드와 멀티스레드에 대해서도 설명한다. 

내가 알고 있는 지식은 하나의 프로세스 안에 여러 개의 스레드가 있을 수 있고, 그 스레드를 한 번에 다중 사용하는 걸 멀티스레드라고 알고 있었다. 책의 정의는 다음과 같다.

 

스레드

- 운영체제에서 작동되는 스케줄링될 수 있는 인스트럭션(instruction)의 순차적인 흐름.

- 일반적으로 프로세스에 속해 있으며 프로그램 카운터, 스택, 레지스터를 비롯해 식별자로 구성

- 스레드는 프로세서가 시간을 할당할 수 있는 최소 단위의 실행

- 공유 자원 사이에서 상호작용이 가능. 다수의 스레드끼리 통신도 가능. 메모리 공유도 가능

- 메모리 주소에서 읽기 및 쓰기도 가능하지만 2개의 스레드가 메모리를 공유하고 스레드의 실행 순서가 따로 정의되지 않았을 경우 이상한 값이 도출되거나 시스템 충돌 문제를 일으킬 수 있다. 이에 관련된 용어는 레이스 컨디션(race condition, 경합 조건)이다.

- 사용자 레벨 스레드 : 다양한 작업에서 생성, 실행, 종료가 이뤄지는 스레드

- 커널 레벨 스레드 : 운영체제의 하위 레벨에서 실행되는 스레드

 

멀티스레딩

- 단일 스레드는 한 사람이 모든 작업을 차례대로 처리한다고 보면 되는데, 작업자가 특정 작업에 어려움이 있으면 더 이상 다른 작업을 진행할 수 없게 된다.

- 멀티스레딩에서는 한 명의 작업자가 시간을 쪼개서 여러 작업을 진행하는 멀티 테스커가 된다고 보면 된다. 특정 작업을 하다가 막힐 경우 콘텍스트를 바꿔 다른 작업을 진행할 수 있다.

- 스레딩의 장점 : 다수의 스레드는 연산은 간단하나 입출력이 많을 때 속도가 떨어지는 현상이 발생할 때 속도를 획기적으로 높여줄 수 있다. 프로세서와 비교했을 때, 메모리를 조금 차지한다. 자원을 공유하므로 스레드 간 통신이 쉽다.

- 스레딩의 단점 : CPython 스레드는 GIL(Global Interpreter Lock, 한 번에 하나의 스레드만 수행할 수 있는 락)로 인해 사용에 제약이 따른다. 스레드 간의 통신은 쉬워졌으나 레이스 컨디션이 발생하지 않도록 주의해서 코드를 작성해야 한다. 다수의 스레드 간에 컨텍스트를 바꾸는 데 수많은 계산이 필요하다. 다수의 스레드를 추가하면 전반적인 프로그램 성능은 저하될 수 있다.

 

프로세스 

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

- 하나의 주 스레드만 가지고 있을 수도 있고 여러개의 스레드를 갖고 있을 수도 있음. 각 스레드마다 레지스터와 스택이 따로 구성

- 더 많은 성능 필요 시 실행 속도를 높일 수 있으나 크로스 프로세스 통신과 관련된 문제가 발생할 수 있고 프로세스 간 통신(IPC)에서 시간을 낭비해 성능이 저하되지 않도록 유의해야 함.

- GIL의 한계를 피할 수 있으며 프로세스의 충돌은 전체 프로그램에 영향을 주지 않음

 

멀티프로세싱

- 파이썬의 멀티프로세싱 모듈을 사용하면 모든 코어와 CPU 사용 가능.

- GIL이 CPython에서 문제되는 부분을 피할 수 있음

- 공유 상태가 없고 통신이 부족하다는 본질적 문제 존재. 통신을 위해 IPC 형태를 사용할 경우 성능에 영향 미칠 수 있음

- 내부적 레이스 컨디션에 신경을 쓰지 않아도 됨

 

이벤트 기반 프로그래밍

- 어떤 이벤트(동작, 마우스 클릭 등)이 발생했을 경우 관련 부분을 실행하는 형태의 메커니즘

- 이벤트를 발생시키는 이벤트 이미터가 있으며 이벤트 루프는 이벤트를 가져와서 미리 정의된 이벤트 핸들러와 매칭

- 콜백은 비동기 방식의 프로그램에서 사용됨

 

반응형 프로그래밍

- RxPY : 옵서버 패턴, 반복자 패턴, 함수형 프로그래밍의 결합. 예제를 실행하려면 rx==1.6.1버전으로 해야 정상 동작함.

 

GPU 프로그래밍

- GPU는 일반적으로 수백만 연산을 병렬로 하는 작업에 매우 좋은 성능을 보임

- CPU는 데스크의 컨텍스트를 빠른 속도로 바꿀 수 있도록 디자인됨.

- 고성능의 그래픽 카드를 그래픽 프로그래밍에 활용하려면 PyCUDA(Nvidia 계열), OpenCL(범용), Theano(C로 구현)을 사용

 

파이썬의 한계 : GIL

- GIL(Global Interpreter Lock) : 병렬 파이썬 코드를 실행할 때 여러 스레드의 사용을 제한하는 상호 배제 락(mutual exclusion lock)

- 한 번에 1개의 스레드만 유지하는 락으로 스레드를 자체 코드에서 실행하려면 일단 락을 먼저 점검해야 함. 그러므로 락이 되어 있는 동안에는 다른 모든 실행은 불가

- 무작위 라운드 로빈 방식으로 작업을 배치하므로, 어떤 스레드가 락은 먼저 점유할지 파악 불가

- GIL을 제거하려는 노력은 몇 번 있었으나 안정적인 스레드를 보장하는 락을 추가하면 2배 이상의 성능 감소로 이어지게 되어 아직 다른 대안은 없다.

- 아예 GIL을 사용하지 않는 라이브러리도 존재한다.

- Jython은 자파 플랫폼에서 작동 가능한 파이썬 형태, IronPython은 닷넷 프레임워크의 상위에서 동작하여 닷넷 애플리케이션을 보완하는 방식으로 사용 가능

 

이후에는 동시에 그림 다운로드하기, 멀티프로세싱으로 소수 찾기에 대한 예제가 소개된다.

반응형
Comments