Windows와 Linux의 Python 멀티프로세서 차이

파이톤을 떠나지 않고 코드를 가속시키려면 멀티프로세서가 좋은 패키지입니다.내가 멀티프로세서를 사용하기 시작했을 때, 나는 Windows와 Linux 간의 차이를 몰랐다. 이것은 나로 하여금 비교적 큰 프로젝트에서 몇 주 동안의 개발 시간을 지체하게 했다.멀티 프로세싱이 어떻게 작동하는지, Windows 및 Linux의 차이점을 빠르게 살펴보겠습니다.
멀티 프로세싱을 사용하는 가장 빠른 방법을 보여 주는 것은 마스터 프로그램을 차단하지 않고 간단한 함수를 실행하는 것입니다.
import multiprocessing as mp
from time import sleep


def simple_func():
    print('Starting simple func')
    sleep(1)
    print('Finishing simple func')


if __name__ == '__main__':
    p = mp.Process(target=simple_func)
    p.start()
    print('Waiting for simple func to end')
    p.join()
출력은 다음과 같습니다.
Waiting for simple func to end
Starting simple func
Finishing simple func
수출은 우리가 바라는 것이다.이 코드의 행동을 검토함으로써 현재 문제의 핵심을 살펴보겠습니다.
import multiprocessing as mp
from time import sleep


print('Before defining simple_func')

def simple_func():
    print('Starting simple func')
    sleep(1)
    print('Finishing simple func')


if __name__ == '__main__':
    p = mp.Process(target=simple_func)
    p.start()
    print('Waiting for simple func to end')
    p.join()
Windows에서 이 코드를 실행하면 다음과 같은 출력이 제공됩니다.
Before defining simple_func
Waiting for simple func to end
Before defining simple_func
Starting simple func
Finishing simple func
Linux에서는 다음과 같은 출력이 제공됩니다.
Before defining simple_func
Waiting for simple func to end
Starting simple func
Finishing simple func
두 번째Before defining simple_func를 제외하고는 그다지 닮지 않은 것처럼 보이지만 이 차이는 매우 중요하다.Linux에서 하위 프로세스를 시작할 때 갈라집니다.이것은 하위 프로세스가 부모 프로세스의 메모리 상태를 계승한다는 것을 의미한다.그러나 Windows와 기본적으로 Mac에서는 프로세스가 발생합니다.이것은 새로운 해석기가 시작되고 코드가 다시 실행된다는 것을 의미한다.
이것은 우리가 Windows에서 코드를 실행하면 두 배의 결과를 얻을 수 있는 이유Before defining simple_func를 설명한다.보시다시피, 만약 우리가 파일 끝에 포함되지 않는다면 if __main__ 상황은 더욱 나빠질 수 있습니다. 한번 검사해 보겠습니다.Windows에서는 다음과 같은 긴 오류가 발생합니다.
RuntimeError: 
        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes and you have forgotten to use the proper idiom
        in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

        The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce an executable.
Linux에서 잘 작동합니다.그것은 보기에는 그다지 닮지 않을 수도 있지만, 비용이 많이 드는 초기화 작업을 계산해 보세요.아마도 프로그램이 실행될 때 시스템 검사를 할 것이다.시작된 모든 프로세스에 대해 이 검사를 실행하고 싶지 않을 수도 있습니다.런타임 시 값이 변경되면 더 재미있어집니다.
import multiprocessing as mp
import random

val = random.random()

def simple_func():
    print(val)


if __name__ == '__main__':
    print('Before multiprocessing: ')
    simple_func()
    print('After multiprocessing:')
    p = mp.Process(target=simple_func)
    p.start()
    p.join()
Windows의 출력은 다음과 같습니다.
Before multiprocessing:
0.16042209710776734
After multiprocessing:
0.9180213870647225
Linux에서는 다음과 같은 출력을 제공합니다.
Before multiprocessing:
0.28832424513226507
After multiprocessing:
0.28832424513226507
이것은 우리가 리눅스로 작성한 코드를 윈도에 이식해서 일할 때 왜 이렇게 많은 시간을 낭비했는지 마지막 화제로 가져왔다.값이 운행할 때 변화하는 전형적인 상황은 처리 클래스에 있을 때이다.물체는 가치를 보존하는 데 쓰인다.그것들은 정적이 아니다.만약 당신이 하나의 단독 프로세스에서 하나의 종류를 실행하려고 시도한다면 어떤 일이 일어날까요?간단한 작업부터 시작하겠습니다.
import multiprocessing as mp


class MyClass:
    def __init__(self, i):
        self.i = i

    def simple_method(self):
        print('This is a simple method')
        print(f'The stored value is: {self.i}')

    def mp_simple_method(self):
        self.p = mp.Process(target=self.simple_method)
        self.p.start()

    def wait(self):
        self.p.join()


if __name__ == '__main__':
    my_class = MyClass(1)
    my_class.mp_simple_method()
    my_class.wait()
이 코드는 Linux와 Windows에서 모두 정상적으로 작동합니다.이것은 여러 가지 다른 장면에서 발생할 수 있다. 언젠가는 파일에서 쓰거나 읽는 등 약간 복잡한 일을 시도해 보게 될 것이다.
import multiprocessing as mp


class MyClass:
    def __init__(self, i):
        self.i = i
        self.file = open(f'{i}.txt', 'w')

    def simple_method(self):
        print('This is a simple method')
        print(f'The stored value is: {self.i}')

    def mp_simple_method(self):
        self.p = mp.Process(target=self.simple_method)
        self.p.start()

    def wait(self):
        self.p.join()
        self.file.close()


if __name__ == '__main__':
    my_class = MyClass(1)
    my_class.mp_simple_method()
    my_class.wait()
Linux에서 위의 코드가 잘 작동합니다.그러나 Windows와 Mac에서 심각한 오류가 발생할 수 있습니다.
[...]
    ForkingPickler(file, protocol).dump(obj)
TypeError: cannot serialize '_io.TextIOWrapper' object
우리는 이 서류에 대해 아무것도 하지 않는다는 것을 주의해 주십시오.우리는 단지 그것을 열고 그것을 속성으로 클래스에 저장할 뿐이다.그러나 이 잘못은 흥미로운 특성을 가리키고 있다.산란의 작업 방식은 전체 대상을 산세척하는 것이다.따라서 선택할 수 없는 클래스나 속성이 있으면 하위 프로세스를 시작할 수 없습니다.
그리고 하드웨어를 사용하는 사람들에게 가장 가능성이 높은 것은 파일은 픽업할 수 없는 것처럼 장치와의 통신이다.자물쇠나 이런 것들을 실현함으로써 다처리의 안전성을 확보하기 위해 아무리 노력해도근본적인 문제는 비교적 낮은 차원에 있다.

해결할 방법이 있습니까?
유감스럽게도 Windows에서 프로세스를 시작하는 방식을 변경할 수 없습니다.다른 한편, 리눅스에서 프로세스가 시작되는 방식을 변경할 수 있습니다.프로그램도 Windows와 Mac에서 실행될 수 있도록 해 줍니다.다음만 추가하면 됩니다.
if __name__ == '__main__':
    mp.set_start_method('spawn')
    my_class = MyClass(1)
    my_class.mp_simple_method()
    my_class.wait()
set_start_method를 사용하면 Windows 및 Linux에서 동일한 오류가 발생합니다.이 줄을 추가할 필요가 있는지 없는지는 당신이 무엇을 실현하느냐에 달려 있다.
따라서 만약 당신이 이러한 차이에 부딪히게 된다면, 당신은 어쩔 수 없이 당신의 프로그램 설계를 다시 생각해야 할 것이다.속성, 특히 장치 및 ZMQ 소켓에 대한 드라이버를 선택할 수 없습니다.

속도가 또 다른 요인이에요.
비록 프로세스는 통상적으로 컴퓨터의 여러 개의 핵을 이용하여 프로그램의 속도를 가속화하지만, 모든 프로세스를 시작하는 데는 시간이 많이 걸릴 수 있다.Windows와 Mac에서 파이톤은 하위 프로세스를 만들기 위해 pickle 대상을 필요로 한다. 이 사실은 비용을 증가시켜 단독 프로세스에서 실행되는 장점을 상쇄할 수 있다.몇 시간 동안 실행되는 임무가 아니라 작은 임무가 많을 때 이 점은 특히 중요하다.
따라서 사용 과정에서 프로그램의 속도를 높이는 것은 당연한 결과가 아니다.응용 프로그램에 대해 항상 기준 테스트를 해서 서로 다른 구성 요소가 어디에 있는지, 그리고 그 행동에 어떻게 영향을 미치는지 알아야 합니다.

좋은 웹페이지 즐겨찾기