Advertisement

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

NAVER D2
Sep. 29, 2014
Advertisement

More Related Content

Slideshows for you(20)

Advertisement

More from NAVER D2(20)

Recently uploaded(20)

Advertisement

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

  1. Python 동시성/병렬성 DEVIEW 2014 정민영 @ 비트패킹컴퍼니
  2. 발표자 • 정민영 • 미투데이 / 비트패킹컴퍼니 • 07년 Nginx를 만난 후 비동기 덕후 • kkung@beatpacking.com
  3. 우리는
  4. 매우 사랑하긴 하지만…
  5. def calc(): return max([random() for x in xrange(20000000)]) ! if __name__ == '__main__': print(calc()) print(calc()) 4.418s
  6. 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
  7. 가끔은…. 당황!
  8. 왜 thread가 더 느리지?
  9. Global Interpreter Lock
  10. Thread 1
  11. Thread 1 Thread 2
  12. Thread 1 Thread 2 Thread 3
  13. Thread 1 Thread 2 Thread 3 3개의 Python thread가 수행되고 있는 상태에서, Thread 1이 수행되고 있는 상황의 예를 보겠습니다.
  14. Thread 1 Thread 2 Thread 3 I/O
  15. Thread 1 Thread 2 Thread 3 I/O release GIL
  16. Thread 1 Thread 2 Thread 3 I/O release GIL acquire GIL
  17. Thread 1 Thread 2 Thread 3 I/O release GIL Context Switch acquire GIL Python은, I/O(/System call)이 발생하게 되면, thread는 GIL을 해제하게 되고, 다른 GIL을 획득 하는 thread로 제어권이 넘어갑니다.
  18. Thread 1 Thread 2 Thread 3 I/O release GIL Context Switch acquire GIL Python은, I/O와 같은 경우 처럼 Python 객체를 건드리지 않고, 계속 수행할 필요가 있을 경우 Py_BEGIN_ALLOW_THREADS 를 통해서, 계속 thread를 수행하게 할 수 있습니다.
  19. 또한, system call뿐만 아니라, Python byte code 상에서 100개가 실행될 때마다도 마찬가지로 GIL을 해제함으로써, 다른 thread로 제어권을 넘기게 됩니다. Thread 1 Thread 2 Thread 3 I/O 100 tick release GIL C/S acquire GIL
  20. Python은 사실상 Single Thread 따라서, Python은 threading 모듈을 통해서 여러 thread 를 생성할 수 있음에도 불구하고, 사실상 GIL에 의해서 예 외적인 경우를 제외하고, 한 시점에는 하나의 thread만 작 동하는 것 처럼 보이게 됩니다.
  21. I/O는 되는거 아닌가요?
  22. Thread 1 Thread 2 Thread 3 I/O I/O
  23. Thread 1 Thread 2 Thread 3 I/O I/O
  24. Thread 1 Thread 2 Thread 3 I/O I/O C/S ? System call에 의해서 GIL을 획득하지 못한 thread가 계속 수행되는 경우에도, system call이 종료되고 python 코드를 수행하기 위해서는 GIL을 획득해야하기 때문에, 원하는 시점에 thread가 제어권을 다시 획 득하는 보장이 없습니다.
  25. 장담할 수 없음!
  26. Implicit Scheduling
  27. 어떤 thread로 넘어갈지는 사실상 랜덤 Python은 별도의 thread 스케쥴링 없이, 시스템이 제공하는 threading library의 lock mechanism에 의해서 문맥이 전환됩니다.
  28. 그럼, GIL은 무조건 나쁜가요?
  29. 그건 아닙니다.
  30. (상대적으로) 단순한 구현 적은 버그와 용이한 구현
  31. single thread 성능에 우위 Python은 memory 관리를 reference counting의 방법으로 하는데, GIL에 대비되는 fine-grained lock방법을 사용할 경우 모든 객체의 접 근마다 lock 으로 관리해야 하므로 그 overhead가 매우 큽니다.
  32. C 확장 개발에 용이
  33. 더 많은 일을 빠르게 처리하려면?
  34. 일?
  35. CPU BOUND
  36. 수행시간에 CPU가 더 영향이 큰 작업
  37. 압축, 정렬, 인코딩, ….
  38. I/O BOUND
  39. 수행시간에 I/O가 더 영향이 큰 작업
  40. 네트워크, 디스크, ….
  41. 대부분의 WEB APP!
  42. 이런 일을 더 많이, 더 빠르게
  43. 동시성을 높인다
  44. 병렬성을 높인다
  45. 동시성? 병렬성?
  46. 같은말 아닌가요?
  47. 동시성 ≠ 병렬성
  48. PARALLELISM
  49. CONCURRENCY
  50. CPU BOUND 병렬성으로 UP
  51. I/O BOUND 동시성으로 UP
  52. 그럼 python은 병렬성은 물건너 갔나요?
  53. “(…) 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
  54. multiprocessing
  55. Fork
  56. if __name__ == '__main__': processes = [] for i in xrange(2): processes.append(multiprocessing.Process(target=calc)) processes[-1].start() p.join() 2.431s for p in processes:
  57. multiprocessing 모듈은, fork를 통해서 동시에 여러 프로세스에 원하는 작업을 실행할 수 있도록 도와주는 모듈입니다. 앞서 예제 코드를 실행할 경우, main process를 포함해서 추가로 2개의 프로세스가 작동하 여 병렬화 되는 모습을 확인할 수 있습니다.
  58. Queue / Pipe
  59. Lock
  60. Shared Memory / Manager
  61. Pool
  62. 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과 같이 지정한 갯수의 프 로세스들에 작업을 지속적으로 할당하여, 병렬처리를 손쉽 게 할 수 있도록 지원합니다.
  63. 그렇다면 동시성은?
  64. non blocking socket
  65. asyncore
  66. select / poll
  67. asyncore 모듈이나, python 표준에 포함된 select 등은 그래프와 같이 일반적으로 다뤄야 하는 fd(socket)의 수가 증가할수록 비례하여 성능이 저하 되어 효율이 극히 떨어집니다.
  68. 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
  69. connect('deview.kr', function(result, socket) { socket.read(function(data) { socket.write(data, function(result) { socket.close(function(result) { }); }); }); }); 또한, 일반적으로 비동기 방식으로 I/O를 다루는 방법은 동기적으로 I/O를 다루는 방법보다 개발의 복잡도를 높 이고, 코드의 구조를 알아보기 어렵게 합니다.
  70. SHOW TIME
  71. def handle_request(s): try: s.recv(1024) s.send('HTTP/1.0 200 OKrn') s.send('Content-Type: text/plainrn') s.send('Content-Length: 5rn') s.send('rn') s.send('hello') s.close() except Exception, e: logging.exception(e)
  72. 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서버를 모사 한 형태의 테스트 입니다.
  73. Requests
Advertisement