def calc():
return max([random() for x in
xrange(20000000)])
!
if __name__ == '__main__':
print(calc())
print(calc()) 4.418s
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
Thread 1
Thread 2
Thread 3
I/O
release GIL Context Switch
acquire GIL
Python은, I/O(/System call)이 발생하게 되면,
thread는 GIL을 해제하게 되고, 다른 GIL을 획득 하는
thread로 제어권이 넘어갑니다.
Thread 1
Thread 2
Thread 3
I/O
release GIL Context Switch
acquire GIL
Python은, I/O와 같은 경우 처럼 Python 객체를
건드리지 않고, 계속 수행할 필요가 있을 경우
Py_BEGIN_ALLOW_THREADS 를 통해서, 계속
thread를 수행하게 할 수 있습니다.
또한, system call뿐만 아니라, Python byte code
상에서 100개가 실행될 때마다도 마찬가지로 GIL을
해제함으로써, 다른 thread로 제어권을 넘기게 됩니다.
Thread 1
Thread 2
Thread 3
I/O 100 tick
release GIL C/S
acquire GIL
Python은 사실상
Single Thread
따라서, Python은 threading 모듈을 통해서 여러 thread
를 생성할 수 있음에도 불구하고, 사실상 GIL에 의해서 예
외적인 경우를 제외하고, 한 시점에는 하나의 thread만 작
동하는 것 처럼 보이게 됩니다.
Thread 1
Thread 2
Thread 3
I/O I/O
C/S ?
System call에 의해서 GIL을 획득하지 못한 thread가 계속 수행되는
경우에도, system call이 종료되고 python 코드를 수행하기 위해서는
GIL을 획득해야하기 때문에, 원하는 시점에 thread가 제어권을 다시 획
득하는 보장이 없습니다.
single thread
성능에 우위
Python은 memory 관리를 reference counting의 방법으로 하는데,
GIL에 대비되는 fine-grained lock방법을 사용할 경우 모든 객체의 접
근마다 lock 으로 관리해야 하므로 그 overhead가 매우 큽니다.
“(…) 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
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:
multiprocessing 모듈은, fork를 통해서 동시에 여러
프로세스에 원하는 작업을 실행할 수 있도록 도와주는
모듈입니다. 앞서 예제 코드를 실행할 경우, main
process를 포함해서 추가로 2개의 프로세스가 작동하
여 병렬화 되는 모습을 확인할 수 있습니다.
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과 같이 지정한 갯수의 프
로세스들에 작업을 지속적으로 할당하여, 병렬처리를 손쉽
게 할 수 있도록 지원합니다.
connect('deview.kr', function(result, socket) {
socket.read(function(data) {
socket.write(data, function(result) {
socket.close(function(result) {
});
});
});
});
또한, 일반적으로 비동기 방식으로 I/O를 다루는 방법은
동기적으로 I/O를 다루는 방법보다 개발의 복잡도를 높
이고, 코드의 구조를 알아보기 어렵게 합니다.