2011년 5월 11일 수요일

폴링, futures, 병렬 실행에 대하여

원문: Of polling, futures and parallel execution (날짜: 2011-03-24, 작성자: Antoine Pitrou)

현대 컴퓨팅에서 주된 관심사 중의 하나는 전원을 절약하는 것에 있습니다. 이것은 휴대용 기기(랩톱, 태블릿, 핸드헬드)에서 중요한 문제가 됩니다. 최신 CPU는 사용하지 않는 동안에는 여러 가지 저전력 상태로 들어갈 수 있습니다. 사용하지 않는 시간이 오래될수록 저전력 상태가 더욱 깊어져서 에너지 소비는 더 적어지므로, 한번의 충전으로 장치의 배터리를 사용할 수 있는 시간은 더욱 길어집니다.

저전력 상태에는 폴링(polling)이라는 방해물이 하나 있습니다. 잠재적인 변화를 확인하기 위해 특정 메모리 위치를 읽는 것과 같은 사소한 작업일지라도 주기적으로 CPU를 깨우게 되면 CPU는 저전력 상태에서 벗어나 자신의 모든 내부 구조물을 가동합니다. 다시 저전력 상태로 돌아가려면 할 일이 다 끝나도 한참이 지나야 할 것입니다. 이로 인해 배터리 사용 시간은 줄어듭니다. Intel도 이 문제를 염려하고 있습니다.

파이썬 3.2에는 동시에 작업을 시작시켜 끝날 때까지 기다리는 새로운 표준 모듈이 도입되었습니다. 그것은 바로 concurrent.futures 모듈입니다. 그 코드를 자세히 살펴보니, 워커(worker) 스레드와 프로세스 일부에서 폴링을 사용하고 있음을 알게 되었습니다. ThreadPoolExecutorProcessPoolExecutor는 구현이 다르기 때문에 "일부"라고 했습니다. 전자는 워커 스레드 각각에서 폴링을 하는 반면 후자는 워커 프로세스와 통신하는 큐 관리(queue management) 스레드 하나에서만 그렇게 하고 있었습니다.

여기에서 폴링은 종료(shutdown) 절차가 시작되었는지 감지하기 위한 목적으로만 사용되었습니다. 콜러블(callable)을 큐에 넣는 것이나 전에 큐에 들어갔던 콜러블에서 결과를 가져오는 것과 같은 다른 작업들은 동기화 큐(synchronized queue) 객체를 사용합니다. 이 큐 객체는 사용하는 수행기(executor) 구현에 따라 threading이나 multiprocessing 모듈 중 하나에 있던 것입니다.

그래서 나는 간단한 해결책을 제안했습니다. 폴링 대신 None이라는 내장 센티널(sentinel)을 사용하는 것입니다. 큐가 None을 받으면 대기 중인 워커 중 하나가 자연스럽게 깨어나서 종료해야 하는지 아닌지 확인하게 됩니다. ProcessPoolExecutor에서는 하나의 큐 관리 스레드와 더불어 N개의 워커 프로세스를 깨워야 할 필요가 있기 때문에 약간 복잡합니다.

나의 처음 패치에서는 여전히 폴링 타임아웃이 있었습니다. 워커가 언젠가는 깨어날 수 있도록 매우 길게(10분) 잡아 두었습니다. 이렇게 긴 시간의 타임아웃은 코드에 버그가 있어 종료해야 할 때임에도 센티널을 통하여 종료 통지를 받지 못했을 때 생깁니다. 호기심에서 multiprocessing의 소스 코드를 살펴보았더니 또 다른 흥미로운 사실을 발견하였습니다. Windows에서는 multiprocessing.Queue.get()에 0이 아닌 유한한 타임아웃을 주면 폴링을 사용합니다. (내가 이슈 11668으로 올림) 타임아웃이 1 밀리초에서 시작하여 매 루프가 반복될 때마다 증가하기 때문에 흥미롭게도 매우 빈번하게 폴링이 사용됩니다.

이렇게 타임아웃이 구현된 방법이 매 밀리초마다 깨어나도록 되어 있기 때문에, 길긴 하지만 여전히 타임아웃을 사용하고 있던 나의 패치가 Windows에서는 쓸모 없어졌음은 말할 필요가 없습니다. 그래서 나는 이를 악물고 그 긴 폴링 타임아웃을 제거하기로 했습니다. 나의 최종 패치는 타임아웃을 전혀 사용하지 않게 되어 어느 플랫폼에서도 주기적으로 깨어나는 일이 생기지 않습니다.

역사적인 이야기를 하자면, 파이썬 3.2 이전에는 threading 모듈에서 모든 타임아웃 기능에 폴링을 사용하였고 multiprocessing 자체가 여러 작업에 워커 스레드를 사용하기 때문에 multiprocessing의 많은 부분에서 폴링을 사용하게 되었습니다. 이 문제는 이슈 7316에서 고쳐졌습니다.

댓글 없음:

댓글 쓰기