146
Python 동시성/병렬성 DEVIEW 2014 정민영 @ 비트패킹컴퍼니

[2D4]Python에서의 동시성_병렬성

Embed Size (px)

DESCRIPTION

DEVIEW 2014 [2D4]Python에서의 동시성_병렬성

Citation preview

Page 1: [2D4]Python에서의 동시성_병렬성

Python 동시성/병렬성DEVIEW 2014

정민영 @ 비트패킹컴퍼니

Page 2: [2D4]Python에서의 동시성_병렬성

발표자

• 정민영

• 미투데이 / 비트패킹컴퍼니

• 07년 Nginx를 만난 후 비동기 덕후

[email protected]

Page 3: [2D4]Python에서의 동시성_병렬성

우리는

Page 4: [2D4]Python에서의 동시성_병렬성
Page 5: [2D4]Python에서의 동시성_병렬성

매우 사랑하긴 하지만…

Page 6: [2D4]Python에서의 동시성_병렬성

def calc(): return max([random() for x in xrange(20000000)]) !if __name__ == '__main__': print(calc()) print(calc()) 4.418s

Page 7: [2D4]Python에서의 동시성_병렬성

if __name__ == '__main__': threads = [] for i in xrange(2): threads.append(threading.Thread(target=calc)) threads[-1].start() for t in threads: t.join()

7.683s

Page 8: [2D4]Python에서의 동시성_병렬성
Page 9: [2D4]Python에서의 동시성_병렬성

가끔은…. 당황!

Page 10: [2D4]Python에서의 동시성_병렬성

왜 thread가 더 느리지?

Page 11: [2D4]Python에서의 동시성_병렬성

Global Interpreter Lock

Page 12: [2D4]Python에서의 동시성_병렬성

Thread 1

Page 13: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Page 14: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

Page 15: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

3개의 Python thread가 수행되고 있는 상태에서, Thread 1이 수행되고 있는 상황의 예를 보겠습니다.

Page 16: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

I/O

Page 17: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

I/O

release GIL

Page 18: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

I/O

release GIL

acquire GIL

Page 19: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

I/O

Context Switchrelease GIL

acquire GIL

Python은, I/O(/System call)이 발생하게 되면, thread는 GIL을 해제하게 되고, 다른 GIL을 획득 하는

thread로 제어권이 넘어갑니다.

Page 20: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

I/O

Context Switchrelease GIL

acquire GIL

Python은, I/O와 같은 경우 처럼 Python 객체를 건드리지 않고, 계속 수행할 필요가 있을 경우

Py_BEGIN_ALLOW_THREADS 를 통해서, 계속 thread를 수행하게 할 수 있습니다.

Page 21: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

I/O 100 tick

C/Srelease GIL

acquire GIL

또한, system call뿐만 아니라, Python byte code상에서 100개가 실행될 때마다도 마찬가지로 GIL을 해제함으로써, 다른 thread로 제어권을 넘기게 됩니다.

Page 22: [2D4]Python에서의 동시성_병렬성

Python은 사실상 Single Thread

따라서, Python은 threading 모듈을 통해서 여러 thread를 생성할 수 있음에도 불구하고, 사실상 GIL에 의해서 예외적인 경우를 제외하고, 한 시점에는 하나의 thread만 작

동하는 것 처럼 보이게 됩니다.

Page 23: [2D4]Python에서의 동시성_병렬성

I/O는 되는거 아닌가요?

Page 24: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

I/O I/O

Page 25: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

I/O I/O

Page 26: [2D4]Python에서의 동시성_병렬성

Thread 1

Thread 2

Thread 3

I/O I/O

C/S ?

System call에 의해서 GIL을 획득하지 못한 thread가 계속 수행되는 경우에도, system call이 종료되고 python 코드를 수행하기 위해서는 GIL을 획득해야하기 때문에, 원하는 시점에 thread가 제어권을 다시 획

득하는 보장이 없습니다.

Page 27: [2D4]Python에서의 동시성_병렬성

장담할 수 없음!

Page 28: [2D4]Python에서의 동시성_병렬성

Implicit Scheduling

Page 29: [2D4]Python에서의 동시성_병렬성

어떤 thread로 넘어갈지는 사실상 랜덤

Python은 별도의 thread 스케쥴링 없이, 시스템이 제공하는 threading library의 lock mechanism에 의해서 문맥이 전환됩니다.

Page 30: [2D4]Python에서의 동시성_병렬성

그럼, GIL은 무조건 나쁜가요?

Page 31: [2D4]Python에서의 동시성_병렬성

그건 아닙니다.

Page 32: [2D4]Python에서의 동시성_병렬성

(상대적으로) 단순한 구현 적은 버그와 용이한 구현

Page 33: [2D4]Python에서의 동시성_병렬성

single thread 성능에 우위

Python은 memory 관리를 reference counting의 방법으로 하는데, GIL에 대비되는 fine-grained lock방법을 사용할 경우 모든 객체의 접

근마다 lock 으로 관리해야 하므로 그 overhead가 매우 큽니다.

Page 34: [2D4]Python에서의 동시성_병렬성

C 확장 개발에 용이

Page 35: [2D4]Python에서의 동시성_병렬성
Page 36: [2D4]Python에서의 동시성_병렬성

더 많은 일을 빠르게 처리하려면?

Page 37: [2D4]Python에서의 동시성_병렬성

일?

Page 38: [2D4]Python에서의 동시성_병렬성

CPU BOUND

Page 39: [2D4]Python에서의 동시성_병렬성

수행시간에 CPU가 더 영향이 큰 작업

Page 40: [2D4]Python에서의 동시성_병렬성

압축, 정렬, 인코딩, ….

Page 41: [2D4]Python에서의 동시성_병렬성

I/O BOUND

Page 42: [2D4]Python에서의 동시성_병렬성

수행시간에 I/O가 더 영향이 큰 작업

Page 43: [2D4]Python에서의 동시성_병렬성

네트워크, 디스크, ….

Page 44: [2D4]Python에서의 동시성_병렬성

대부분의 WEB APP!

Page 45: [2D4]Python에서의 동시성_병렬성

이런 일을 더 많이, 더 빠르게

Page 46: [2D4]Python에서의 동시성_병렬성

동시성을 높인다

Page 47: [2D4]Python에서의 동시성_병렬성

병렬성을 높인다

Page 48: [2D4]Python에서의 동시성_병렬성

동시성? 병렬성?

Page 49: [2D4]Python에서의 동시성_병렬성

같은말 아닌가요?

Page 50: [2D4]Python에서의 동시성_병렬성

동시성 ≠ 병렬성

Page 51: [2D4]Python에서의 동시성_병렬성

PARALLELISM

Page 52: [2D4]Python에서의 동시성_병렬성

CONCURRENCY

Page 53: [2D4]Python에서의 동시성_병렬성

CPU BOUND 병렬성으로 UP

Page 54: [2D4]Python에서의 동시성_병렬성

I/O BOUND 동시성으로 UP

Page 55: [2D4]Python에서의 동시성_병렬성

그럼 python은 병렬성은 물건너 갔나요?

Page 56: [2D4]Python에서의 동시성_병렬성

“(…) If you want your application to make better use of the computational

resources of multi-core machines, you are advised to use multiprocessing. (…)”

https://docs.python.org/2/library/threading.html

Page 57: [2D4]Python에서의 동시성_병렬성

multiprocessing

Page 58: [2D4]Python에서의 동시성_병렬성
Page 59: [2D4]Python에서의 동시성_병렬성

Fork

Page 60: [2D4]Python에서의 동시성_병렬성

if __name__ == '__main__': processes = [] for i in xrange(2): processes.append(multiprocessing.Process(target=calc)) processes[-1].start() for p in processes: p.join() 2.431s

Page 61: [2D4]Python에서의 동시성_병렬성

multiprocessing 모듈은, fork를 통해서 동시에 여러 프로세스에 원하는 작업을 실행할 수 있도록 도와주는 모듈입니다. 앞서 예제 코드를 실행할 경우, main

process를 포함해서 추가로 2개의 프로세스가 작동하여 병렬화 되는 모습을 확인할 수 있습니다.

Page 62: [2D4]Python에서의 동시성_병렬성

Queue / Pipe

Page 63: [2D4]Python에서의 동시성_병렬성

Lock

Page 64: [2D4]Python에서의 동시성_병렬성

Shared Memory / Manager

Page 65: [2D4]Python에서의 동시성_병렬성

Pool

Page 66: [2D4]Python에서의 동시성_병렬성

from multiprocessing import cpu_count, Pool !if __name__ == '__main__': pool = Pool(processes=cpu_count()) for url in ('http://deview.kr', 'http://beatpacking.com', 'http://kkung.net'): pool.apply_async(fetch, url)

Pool 객체는, 마치 thread pool과 같이 지정한 갯수의 프로세스들에 작업을 지속적으로 할당하여, 병렬처리를 손쉽

게 할 수 있도록 지원합니다.

Page 67: [2D4]Python에서의 동시성_병렬성

그렇다면 동시성은?

Page 68: [2D4]Python에서의 동시성_병렬성

non blocking socket

Page 69: [2D4]Python에서의 동시성_병렬성

asyncore

Page 70: [2D4]Python에서의 동시성_병렬성

select / poll

Page 71: [2D4]Python에서의 동시성_병렬성
Page 72: [2D4]Python에서의 동시성_병렬성

asyncore 모듈이나, python 표준에 포함된 select 등은 그래프와 같이 일반적으로 다뤄야 하는

fd(socket)의 수가 증가할수록 비례하여 성능이 저하되어 효율이 극히 떨어집니다.

Page 73: [2D4]Python에서의 동시성_병렬성
Page 74: [2D4]Python에서의 동시성_병렬성

class Handler(asyncore.dispatcher): def __init__(self, host): pass ! def handle_connect(self): pass ! def handle_close(self): pass ! def handle_read(self): pass ! def handle_write(self): pass

Page 75: [2D4]Python에서의 동시성_병렬성

connect('deview.kr', function(result, socket) { socket.read(function(data) { socket.write(data, function(result) { socket.close(function(result) { }); }); }); });

또한, 일반적으로 비동기 방식으로 I/O를 다루는 방법은 동기적으로 I/O를 다루는 방법보다 개발의 복잡도를 높

이고, 코드의 구조를 알아보기 어렵게 합니다.

Page 76: [2D4]Python에서의 동시성_병렬성
Page 77: [2D4]Python에서의 동시성_병렬성

SHOW TIME

Page 78: [2D4]Python에서의 동시성_병렬성

def handle_request(s): try: s.recv(1024) s.send('HTTP/1.0 200 OK\r\n') s.send('Content-Type: text/plain\r\n') s.send('Content-Length: 5\r\n') s.send('\r\n') s.send('hello') s.close() except Exception, e: logging.exception(e)

Page 79: [2D4]Python에서의 동시성_병렬성

def test(): s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('0.0.0.0', 8000)) s.listen(512) ! while True: cli, addr = s.accept() logging.info('accept ', addr) t = threading.Thread(target=handle_request, args=(cli, )) t.daemon = True t.start()

클라이언트의 요청을 받으면, 지정된 응답을 내려주는 HTTP서버를 모사한 형태의 테스트 입니다.

Page 80: [2D4]Python에서의 동시성_병렬성

Requests����������� ������������������  per����������� ������������������  second:����������� ������������������  ����������� ������������������  ����������� ������������������  ����������� ������������������  2099.21����������� ������������������  [#/sec]����������� ������������������  (mean)����������� ������������������  !

Time����������� ������������������  per����������� ������������������  request:����������� ������������������  ����������� ������������������  ����������� ������������������  ����������� ������������������  ����������� ������������������  ����������� ������������������  ����������� ������������������  487.803����������� ������������������  [ms]����������� ������������������  (mean)

Page 81: [2D4]Python에서의 동시성_병렬성

Requests����������� ������������������  per����������� ������������������  second:����������� ������������������  ����������� ������������������  ����������� ������������������  ����������� ������������������  4504.64����������� ������������������  [#/sec]����������� ������������������  (mean)����������� ������������������  !

Time����������� ������������������  per����������� ������������������  request:����������� ������������������  ����������� ������������������  ����������� ������������������  ����������� ������������������  ����������� ������������������  ����������� ������������������  ����������� ������������������  227.321����������� ������������������  [ms]����������� ������������������  (mean)

Page 82: [2D4]Python에서의 동시성_병렬성

A B

RPS 2099 4504 214%

TPR 487 227 214%gevent를 적용하기 전(A)과 gevent(moneky patch)

를 적용한 후 (B)의 성능 비교

Page 83: [2D4]Python에서의 동시성_병렬성
Page 84: [2D4]Python에서의 동시성_병렬성

gevent

Page 85: [2D4]Python에서의 동시성_병렬성

scheduler + !

Page 86: [2D4]Python에서의 동시성_병렬성

scheduler + event loop +

Page 87: [2D4]Python에서의 동시성_병렬성

scheduler + event loop +

python stdlib interface

Page 88: [2D4]Python에서의 동시성_병렬성

python code

gevent

greenlet libev

Kernel

Page 89: [2D4]Python에서의 동시성_병렬성

python code

gevent

greenlet libev

Kernel

thread event loop

scheduler + stdlib interface api + ….

Page 90: [2D4]Python에서의 동시성_병렬성

libev

Page 91: [2D4]Python에서의 동시성_병렬성

현재 시스템에 가장 적절한 event-loop

시스템 선택

Page 92: [2D4]Python에서의 동시성_병렬성

event loop

Page 93: [2D4]Python에서의 동시성_병렬성

while True: events = wait_for_events() for event in events: handle_event(event)

event loop란, 위 코드와 같이 event가 발생하길 기다렸다가, event가 발생한다면 적절한 event 처리기를 실행하는 구조를 말합니다.

Page 94: [2D4]Python에서의 동시성_병렬성

event?

Page 95: [2D4]Python에서의 동시성_병렬성

I/O

kernel

Page 96: [2D4]Python에서의 동시성_병렬성

I/O

kernel

REQUEST

Page 97: [2D4]Python에서의 동시성_병렬성

I/O

kernel

BLOCK

Page 98: [2D4]Python에서의 동시성_병렬성

I/O

kernel

RESPONSE

보통 I/O 요청은, 커널에 의해서 처리가 완료될때까지 block 됩니다.

Page 99: [2D4]Python에서의 동시성_병렬성

I/O

kernel

event loop

Page 100: [2D4]Python에서의 동시성_병렬성

I/O

kernel

event loop

REQUEST

Page 101: [2D4]Python에서의 동시성_병렬성

I/O

kernel

event loop

REQUEST EVENT

RETURN

Page 102: [2D4]Python에서의 동시성_병렬성

I/O

kernel

event loop

EVENT

Page 103: [2D4]Python에서의 동시성_병렬성

I/O

kernel

event loop

callback

RESPONSE

그러나, event-loop을 사용한 비동기의 방식을 도입하면, I/O요청은 즉시 반환되어 block되지 않고, kernel에 의해서 처리가 완료되는 순간 통지 받도록

할 수 있습니다.

Page 104: [2D4]Python에서의 동시성_병렬성

Greenlet

Page 105: [2D4]Python에서의 동시성_병렬성

A “greenlet”, on the other hand, is a still more primitive notion of micro-thread with no implicit scheduling;

coroutines, in other words.

Page 106: [2D4]Python에서의 동시성_병렬성

coroutine

Page 107: [2D4]Python에서의 동시성_병렬성

진입점이 여러개인 함수

Page 108: [2D4]Python에서의 동시성_병렬성

def hello(): while True: name = (yield) print 'Hello %s' % name !

c = hello() c.next() c.send('kkung') c.send('Deview 2014') !

Hello kkung Hello Deview 2014python의 generator를 이용한 coroutine 예제 입니다. hello() 함수는 무한 루프를 도는 함수임에도 불구하고, 예제와 같이 출력되는 모습을 볼 수 있습니다.

Page 109: [2D4]Python에서의 동시성_병렬성

def hello(): while True: name = (yield) print 'Hello %s' % name !c = hello() c.next() c.send('kkung') c.send('Deview 2014') !Hello kkung Hello Deview 2014

hello 함수는 내부적으로 무한 루프를 돌도록 되어 있으나 yield를 포함하고 있어 generator 함수로 처리되며, c.next()에 의해서 진입합니다.

Page 110: [2D4]Python에서의 동시성_병렬성

def hello(): while True: name = (yield) print 'Hello %s' % name !c = hello() c.next() c.send('kkung') c.send('Deview 2014') !Hello kkung Hello Deview 2014

yield에 의해 문맥은 다시 hello() 함수 바깥으로 전환되고, c.send(‘kkung’) 에 의해서 ‘kkung’이 전달됨과 동시에, 문맥이 hello 함수로 전환됩니다.

Page 111: [2D4]Python에서의 동시성_병렬성

def hello(): while True: name = (yield) print 'Hello %s' % name !c = hello() c.next() c.send('kkung') c.send('Deview 2014') !Hello kkung Hello Deview 2014

hello 함수는, 문맥이 전환됨에 따라(재진입) print문을 실행하게 됩니다.

Page 112: [2D4]Python에서의 동시성_병렬성

Cooperative Multitasking

Page 113: [2D4]Python에서의 동시성_병렬성

명시적인 스케줄링

Page 114: [2D4]Python에서의 동시성_병렬성

MAIN THREAD

Coroutine

Coroutine

Coroutine

Coroutine

greenlet은, 특정 python thread에 귀속되며 실제 thread보다 훨씬 가벼워 많은 수의 greenlet을 저렴하게 생성할 수 있습니다.

Page 115: [2D4]Python에서의 동시성_병렬성

MAIN THREAD

Coroutine

Coroutine

Coroutine

Coroutine

Page 116: [2D4]Python에서의 동시성_병렬성

MAIN THREAD

Coroutine

Coroutine

Coroutine

Coroutine

Page 117: [2D4]Python에서의 동시성_병렬성

MAIN THREAD

Coroutine

Coroutine

Coroutine

Coroutine

Page 118: [2D4]Python에서의 동시성_병렬성

MAIN THREAD

Coroutine

Coroutine

Coroutine

Coroutine

앞서 설명했던 바와 같이 GIL은 시스템의 threading library의 lock mechanism에 의존적이기 때문에 문맥 전환이 암시적입니다. 그러나

greenlet은 그림과 같이 명시적으로 문맥을 전환시킵니다.

Page 119: [2D4]Python에서의 동시성_병렬성

Single Thread 스케줄링

Page 120: [2D4]Python에서의 동시성_병렬성

너무 많은 일을 하면 Xgreenlet은, 앞서 설명한바와 같이 특정 thread에서 귀속되며 그 thread단위로 스케쥴링 됩니다. 이는 특정 greenlet이 CPU를 오래 점유하면 할 수록, 같은 thread에 있는 다른 greenlet들은 idle

상태를 유지한다는 말과 같습니다.

Page 121: [2D4]Python에서의 동시성_병렬성

Sync-like API (NO CALLBACK!)

gevent의 장점중 한가지는, Python에 존재하는 표준 library의 인터페이스를 그대로 변화 없이 사용해도, 비동기적인 처리 효과를 제

공해 준다는 점입니다.

Page 122: [2D4]Python에서의 동시성_병렬성

callbackhell.com

Page 123: [2D4]Python에서의 동시성_병렬성

connect('deview.kr', function(result, socket) { socket.read(function(data) { socket.write(data, function(result) { socket.close(function(result) { }); }); }); });

예를들어, 특정 언어에서는 비동기 처리를 위해서 callback을 연쇄적으로 제공하는 방법(CPS)외에는 선택지가 없는 경우도 있지만….

Page 124: [2D4]Python에서의 동시성_병렬성

s.connect('deview.kr') data = s.read() s.write(data) s.close()

gevent를 사용한다면, Python에서는 이렇게 직관적으로도 비동기 처리를 할 수 있습니다. (특정 언어 비하의 의도는 없습니다^^;)

Page 125: [2D4]Python에서의 동시성_병렬성

connect('deview.kr', function(result, socket) { socket.read(function(data) { socket.write(data, function(result) { socket.close(function(result) { }); }); }); });

s.connect('deview.kr') data = s.read() s.write(data) s.close()

Page 126: [2D4]Python에서의 동시성_병렬성

def recv(self, *args): while True: try: return sock.recv(*args) except error as ex: if ex.args[0] != EWOULDBLOCK: raise self._wait(self._read_event)

Page 127: [2D4]Python에서의 동시성_병렬성

def recv(self, *args): while True: try: return sock.recv(*args) except error as ex: if ex.args[0] != EWOULDBLOCK: raise self._wait(self._read_event)

Page 128: [2D4]Python에서의 동시성_병렬성

def recv(self, *args): while True: try: return sock.recv(*args) except error as ex: if ex.args[0] != EWOULDBLOCK: raise self._wait(self._read_event)

이것이 가능한 이유는, gevent는 내부적으로 python library의 인터페이스를 그대로 구현하면서, 각 API를 모두 greenlet에서 수행하기 때문입니다. Non block socket을

이용해서, 요청한 순간 바로 처리할 수 없다면 event-loop에서 처리 가능해질때 greenlet을 깨우도록 기다립니다.

Page 129: [2D4]Python에서의 동시성_병렬성

asyncio

Page 130: [2D4]Python에서의 동시성_병렬성

coroutine

Page 131: [2D4]Python에서의 동시성_병렬성

import asyncio [email protected] def compute(x, y): print("Compute %s + %s ..." % (x, y)) yield from asyncio.sleep(1.0) return x + y [email protected] def print_sum(x, y): result = yield from compute(x, y) print("%s + %s = %s" % (x, y, result)) !loop = asyncio.get_event_loop() loop.run_until_complete(print_sum(1, 2)) loop.close()

Python 3.4 이상부터는, AsyncIO 라이브러리를 이용할 수도 있습니다. 이 라이브러리는, python 3.3에서 도입된 yield from 문법을 이용해서 coroutine을 구현하여 좀 더

pythonic한 방법으로 비동기 처리를 제공합니다.

Page 132: [2D4]Python에서의 동시성_병렬성

import asyncio [email protected] def compute(x, y): print("Compute %s + %s ..." % (x, y)) yield from asyncio.sleep(1.0) return x + y [email protected] def print_sum(x, y): result = yield from compute(x, y) print("%s + %s = %s" % (x, y, result)) !loop = asyncio.get_event_loop() loop.run_until_complete(print_sum(1, 2)) loop.close()

Page 133: [2D4]Python에서의 동시성_병렬성

import asyncio [email protected] def compute(x, y): print("Compute %s + %s ..." % (x, y)) yield from asyncio.sleep(1.0) return x + y [email protected] def print_sum(x, y): result = yield from compute(x, y) print("%s + %s = %s" % (x, y, result)) !loop = asyncio.get_event_loop() loop.run_until_complete(print_sum(1, 2)) loop.close()

Page 134: [2D4]Python에서의 동시성_병렬성

import asyncio [email protected] def compute(x, y): print("Compute %s + %s ..." % (x, y)) yield from asyncio.sleep(1.0) return x + y [email protected] def print_sum(x, y): result = yield from compute(x, y) print("%s + %s = %s" % (x, y, result)) !loop = asyncio.get_event_loop() loop.run_until_complete(print_sum(1, 2)) loop.close()

Page 135: [2D4]Python에서의 동시성_병렬성

앞서, coroutine은 진입점이 여러개인 함수다 라는 표현을 사용하였는데, 위 코드에서 보는 바와 같이 print_sum, compute, asyncio.sleep 이 각 호출되면서도 종료시에

yield from의 다음 줄이 수행되는 모습을 볼 수 있습니다.

Page 136: [2D4]Python에서의 동시성_병렬성

example

Page 137: [2D4]Python에서의 동시성_병렬성

S3에 수백만개의 파일 올리기

Page 138: [2D4]Python에서의 동시성_병렬성

from gevent import monkey monkey.patch_all()

monkey patch는, python 표준 라이브러리들 중 IO에 관계 있는 주요 라이브러리들을, gevent의 그것으로 대체시킵니다. 따라서, monkey patch후에는 기존의 python에서 IO를 다루던 방법 그래도 수행하여도, gevent에 의해서 비동기 처리로 전환됩니다.

Page 139: [2D4]Python에서의 동시성_병렬성

from gevent.pool import Pool pool = Pool(1024)

Page 140: [2D4]Python에서의 동시성_병렬성

def upload_to_s3(file_name): bucket.new_key(file_name)\ .set_contents_from_filename(file_name)

Page 141: [2D4]Python에서의 동시성_병렬성

for file_name in files: pool.spawn(upload_to_s3, file_name) gevent.wait()

Page 142: [2D4]Python에서의 동시성_병렬성

1Gb/s+ / Process

Page 143: [2D4]Python에서의 동시성_병렬성

recap

Page 144: [2D4]Python에서의 동시성_병렬성

CPU가 많이 필요한 일은 multiprocessing

Page 145: [2D4]Python에서의 동시성_병렬성

I/O가 많이 필요한 일은gevent/AsyncIO