프로세스란
하드디스크에 저장된 프로그램이 실행되고, 메모리가 할당된 것을 말한다.
정의는
프로세스 : 실행 중인 프로그램, 자원과 쓰레드로 구성
쓰레드 : 프로세스 내에서 실제 작업을 수행
예를 들면 프로세스는 작업장이고 쓰레드는 노동자다. 운영 중인 작업장이라면 노동자가 많을 수도
있고 한 명일수도 있다
파이썬은 인터프리터 언어로서 기본적으로 싱글 쓰레드에서 순차적으로 동작, 따라서 병렬처리를
위해선 별도의 모듈을 사용해서 수행해야 한다.
일단 주피터 노트북은 멀티 프로세싱이 잘 작동되는 환경이 아니다. 파이참이나 vs코드에서 하자
멀티 쓰레드를 구현해보면
from threading import Thread
def work(work_id, start, end, result):
total = 0
for i in range(start, end):
total += 1
result.append(total)
if __name__ == "__main__":
result = []
th1 = Thread(target=work, args=(1, 0, 10000, result))
th2 = Thread(target=work, args=(2, 10001, 20000, result))
th1.start()
th2.start()
th1.join() # join()메소드는 파이썬에게 프로세스가 종료될 때까지 대가히도록 지시
th2.join()
print(sum(resultx))
__name__ 은 모듈을 불러왔을 땐 그 모듈의 이름이 되고, 그 파일 내에서 실행하면 __main__이
나온다.
그러므로 파일내에서 쓰레드를 실행하면 빈 리스트를 만들고, Thread는 target은 내가 적용시키고
싶은 함수, args는 그 함수에 들어가는 인수다.
위 코드에선 두개의 쓰레드가 동시에 병렬적으로 돌아간다.
중간에 프로세스가 종료되면 쓰레드가 꺼진다. 그래서 join()을 넣어줌
사실 파이썬에서 멀티 쓰레드를 쓴 것과 싱글 쓰레드로 실행시킨 것은 실행시간이 거의 차이나지 않는다.
그건 파이썬의 GIL(Global Interpreter Lcok)정책 때문이다.
단, 이 GIL정책은 cpu 작업에서만 적용되므로, I/O 작업이 많은 병렬처리 작업에서는 멀티 쓰레드가
효과적일 수 있다.
근데 멀티 프로세싱을 하면 파이썬에서도 속도 빠르게 가능
멀티 프로세싱 Pool
from multiprocessing import Pool
def f(x):
return x * x
print(__name__)
if __name__ = "__main__"
p = Pool(4) # pool은 프로세스의 개수
result = p.map(f, range(100000))
p.close()
print(result)
이렇게 실행하면 print(__name__)이 5번 실행된다.
처음 출력되는 것은 __main__이다. 이게 메인 프로세스고,
그 밑에 서브 프로세스가 4개 돌면서 __mp_main__ 이 4번 출력된다.
p = Pool()은 인수의 개수만큼 서브 프로세스를 만들고,
p.map()으로 Pool안에 있는 map()함수를 가져온다. f는 위에서 정의한 함수고 오른쪽에는 콜렉션이
들어가면 실행된다.
근데 if __name__ == "__main__" 을 빼면 엄청나게 많은 에러가 계속 뜬다.
왜 에러가 뜨냐면 재귀함수랑 같은 원리다.
if __name__ == "__main__"이 없으면 메인 프로세스에서 처음 서브 프로세스를 만들고,
그 서브 프로세스에서 다시 서브 프로세스를 만들고, 또 만들어진 서브 프로세스에서 그 작업을
무한 반복한다. 그래서 에러 뜸
멀티 프로세스에선 진짜 중요함
그리고 p.close()로 서브 프로세스를 죽이지 않으면 계속 메모리 차지하며 컴퓨터 엄청 느려짐
from multiprocessing import Pool
import os
import time
def f(x):
print(os.getpid()) # 프로세스의 이름 출력
return x * x
print(__name__)
if __name__ = "__main__":
start = time.time()
p = Pool(4)
result = p.map(f, range(10000000))
p.close()
print(result)
print(time.time()-start)
이 코드를 실행하면 프로세스가 돌 때마다 어떤 프로세스가 돌았는지 확인 가능하다.
작업 수가 적으면 프로세스 하나가 다 처리하는 경우도 있지만, 작업량이 많아지면 프로세스들이
작업량을 분배해서 작업한다.
내 노트북의 경우 pool로 서브 프로세스를 하나 만들었을 때 작업시간이 2.28초였고, 2개일 때
1.55초, 4개일 때 1.37초, 8개일 때 1.40초 걸렸다.
프로세스가 생성되는 시간이 있으니 완전히 반토막 나진 않지만 작업이 복잡하고 시간이 오래 걸릴수록
멀티 프로세스의 성능이 올라간다.
그리고 cpu의 제한이 있으니 프로세스를 너무 많이 만들면 효과적으로 자원을 분배하지 못하니 상황
봐서 적당히 넣는 게 중요하다.
보통 Pool의 개수를 코어의 개수만큼 띄운다.
멀티 프로세싱 Process
import os
from multiprocessing import Process
def f(x):
print(x*x)
if __name__ == '__main__':
numbers = [1, 2, 3, 4]
proc1 = Process(target=f, args=(numbers[0],)) # 튜플이니 뒤에 콤마 넣음
proc1.start()
proc2 = Process(target=f, args=(numbers[1],))
proc2.start()
proc3 = Process(target=f, args=(numbers[2],))
proc3.start()
proc4 = Process(target=f, args=(numbers[3],))
proc4.start()
proc1.join()
proc2.join()
proc3.join()
proc4.join()
Pool과 Process 모두 병렬처리를 위해 사용되지만 차이는 존재한다. 쉽게 설명하면,Pool 은 처리할
일들을 pool에 뿌려놓고 알아서 병렬처리를 하게 만드는 방식이고, Process는 각 프로세스 별로
할당량을 명시적으로 적어두고 그걸 처리하는 방식이다. // 간단하게 설명한 거지 실제론 더 복잡한
차이가 있음
멀티 프로세스는 공통적으로 실행되는 순서를 알 수 없다. 먼저 시작한 게 늦게 끝날 수도 있음
멀티 쓰레드와 멀티 프로세스
쓰레드는 가볍지만 파이썬의 GIL정책으로 인해 I/O 처리를 하는 경우에만 주로 효과적이고,
프로세스는 각자가 고유한 메모리 영역을 가지기 때문에 처음 프로세스를 만들 때 시간이 조금
필요하고 더 많은 메모리를 필요로 하지만 병렬적으로 cpu작업을 할 수 있어서 빠르다.
'파이썬' 카테고리의 다른 글
6주차, 파이썬 db (0) | 2022.01.29 |
---|---|
6주차, 쓰레드, 멀티 프로세스 활용 (0) | 2022.01.29 |
6주차, 직렬화(인코딩) pickle, json (0) | 2022.01.28 |
6주차, 파일입출력, 인코딩, with open as, os.listdir (0) | 2022.01.28 |
6주차, 파이썬 클래스 활용 (0) | 2022.01.27 |
댓글