Choć skrypt robi użyć sporo pamięci nawet z „mniejszych” przykład wartości, odpowiedź na
Does Pythonie sklonować całe moje otoczenie każdorazowo równoległe procesy są uruchamiane, w tym zmiennych nie jest wymagane przez f()? W jaki sposób mogę zapobiec takiemu zachowaniu?
jest to, że robi się w sposób klon środowisko z forking nowy proces, ale jeśli copy-on-write semantyka są dostępne, nie rzeczywista pamięć fizyczna musi być kopiowane, dopóki nie zostanie zapisany do. Na przykład na tym systemie
% uname -a
Linux mypc 4.2.0-27-generiC#32-Ubuntu SMP Fri Jan 22 04:49:08 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
COW
wydaje się być dostępne i stosowane, ale nie może to mieć miejsce w przypadku innych systemów. W systemie Windows jest to ściśle odmienne, ponieważ nowy interpreter Pythona jest wykonywany z poziomu .exe
zamiast rozwidlenia. Odkąd wspomniałeś używając htop
, używasz jakiegoś smaku systemu UNIX lub UNIX, i dostajesz semantykę COW
.
Dla każdej iteracji pętli w procesów symulacji() każdy mają wykorzystania pamięci równą całkowitej pamięci przez kodzie.
Do procesów potomnych będą wyświetlane niemal identyczne wartości od RSS
, ale może to być mylące, ponieważ w większości zajmują tę samą rzeczywistej pamięci fizycznej mapowane do wielu procesów, jeśli zapisy nie występują. Z wersją Pool.map
historia jest nieco bardziej skomplikowana, ponieważ "rozbija to, co możliwe do przetworzenia na wiele porcji, które przesyła do puli procesów jako oddzielne zadania". Przesłanie to ma miejsce po IPC
, a przesłane dane zostaną skopiowane. W twoim przykładzie połączenia funkcji IPC
i 2 ** 20 również dominują użycie procesora. Zastąpienie mapowania pojedynczym mnożeniem wektoryzowanym w simulation
spowodowało, że środowisko wykonawcze skryptu z około 150 do 0.66s na tym komputerze.
Możemy zaobserwować COW
z (nieco) uproszczonego przykładu, który przydziela dużą tablicę i przekazuje go do zrodził procesu tylko do odczytu przetwarzania:
import numpy as np
from multiprocessing import Process, Condition, Event
from time import sleep
import psutil
def read_arr(arr, done, stop):
with done:
S = np.sum(arr)
print(S)
done.notify()
while not stop.is_set():
sleep(1)
def main():
# Create a large array
print('Available before A (MiB):', psutil.virtual_memory().available/1024 ** 2)
input("Press Enter...")
A = np.random.random(2**28)
print('Available before Process (MiB):', psutil.virtual_memory().available/1024 ** 2)
input("Press Enter...")
done = Condition()
stop = Event()
p = Process(target=read_arr, args=(A, done, stop))
with done:
p.start()
done.wait()
print('Available with Process (MiB):', psutil.virtual_memory().available/1024 ** 2)
input("Press Enter...")
stop.set()
p.join()
if __name__ == '__main__':
main()
Wyjście na tym komputerze:
% python3 test.py
Available before A (MiB): 7779.25
Press Enter...
Available before Process (MiB): 5726.125
Press Enter...
134221579.355
Available with Process (MiB): 5720.79296875
Press Enter...
Teraz, jeśli zastąpimy funkcję read_arr
funkcją modyfikującą tablicę:
def mutate_arr(arr, done, stop):
with done:
arr[::4096] = 1
S = np.sum(arr)
print(S)
done.notify()
while not stop.is_set():
sleep(1)
wyniki są bardzo różne:
Available before A (MiB): 7626.12109375
Press Enter...
Available before Process (MiB): 5571.82421875
Press Enter...
134247509.654
Available with Process (MiB): 3518.453125
Press Enter...
pętli for rzeczywiście wymaga więcej pamięci po każdej iteracji, lecz który jest oczywisty: stosów total_results
z mapowania, tak, że ma Przydziel miejsce dla nowej tablicy, aby pomieścić zarówno stare, jak i nowe wyniki, i zwolnij nieużywaną tablicę starych wyników.
Jak duże są wyniki [0] [0]? – snakecharmerb
tak, klonuje cały kontekst programu. –
Powinieneś strzec tych głównych operacji używając 'if __name__ == '__main __':' co najmniej. –