2016-03-17 13 views
8

Próbuję zmniejszyć wymagania pamięci mojego kodu Pythona 3. Teraz każda iteracja pętli for wymaga więcej pamięci niż poprzednia.Dlaczego moja pętla wymaga więcej pamięci w każdej iteracji?

Napisałem kawałek kodu, który ma takie samo zachowanie jak mojego projektu:

import numpy as np 
from multiprocessing import Pool 
from itertools import repeat 


def simulation(steps, y): # the function that starts the parallel execution of f() 
    pool = Pool(processes=8, maxtasksperchild=int(steps/8)) 
    results = pool.starmap(f, zip(range(steps), repeat(y)), chunksize=int(steps/8)) 
    pool.close() 
    return results 


def f(steps, y): # steps is used as a counter. My code doesn't need it. 
     a, b = np.random.random(2) 
     return y*a, y*b 

def main(): 
    steps = 2**20 # amount of times a random sample is taken 
    y = np.ones(5) # dummy variable to show that the next iteration of the code depends on the previous one 
    total_results = np.zeros((0,2)) 
    for i in range(5): 
     results = simulation(steps, y[i-1]) 
     y[i] = results[0][0] 
     total_results = np.vstack((total_results, results)) 

    print(total_results, y) 

if __name__ == "__main__": 
    main() 

Dla każdej iteracji pętli for wątków w symulacji() mają zużycie pamięci równą całkowitej pamięci używane przez mój kod.

Czy Python klonuje całe moje środowisko za każdym razem, gdy uruchamiane są procesy równoległe, w tym zmienne niewymagane przez f()? Jak mogę zapobiec temu zachowaniu?

Idealnie chciałbym, aby mój kod kopiował tylko pamięć wymaganą do wykonania f(), podczas gdy ja mogę zapisać wyniki w pamięci.

+0

Jak duże są wyniki [0] [0]? – snakecharmerb

+0

tak, klonuje cały kontekst programu. –

+0

Powinieneś strzec tych głównych operacji używając 'if __name__ == '__main __':' co najmniej. –

Odpowiedz

2

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.

0

Może powinieneś znać różnicę między thread a process w Operating System. zobacz to What is the difference between a process and a thread.

W pętli for znajduje się processes, a nie threads. Wątki dzielą przestrzeń adresową procesu, który ją utworzył; procesy mają swoją własną przestrzeń adresową.

Można wydrukować identyfikator procesu, typ os.getpid().

+0

To nie jest tak czarno-białe, jak wskazałem. Procesy mogą współużytkować pamięć fizyczną, chociaż mają własne przestrzenie adresowe. –

Powiązane problemy