Pamięć współdzielona w wieloprocesorze

Mam trzy duże listy. Pierwszy zawiera bitarrays (moduł bitarray 0.8.0), a dwa pozostałe zawierają tablice liczb całkowitych.

l1=[bitarray 1, bitarray 2, ... ,bitarray n]
l2=[array 1, array 2, ... , array n]
l3=[array 1, array 2, ... , array n]

Te struktury danych zajmują sporo pamięci RAM (łącznie~16GB).

Jeśli uruchomię 12 podprocesów używając:

multiprocessing.Process(target=someFunction, args=(l1,l2,l3))

Czy to oznacza, że l1, l2 i l3 będą kopiowane dla każdego podprocesu, czy też podprocesy będą współdzielić te listy? Czy aby być bardziej bezpośrednim, będę używał 16GB lub 192GB pamięci RAM?

SomeFunction odczyta wartości z te listy, a następnie wykonuje pewne obliczenia na podstawie odczytanych wartości. Wyniki zostaną zwrócone do procesu macierzystego. Listy l1, l2 i l3 nie będą modyfikowane przez jakąś funkcję.

Dlatego zakładam, że podprocesy nie potrzebują i nie kopiują tych ogromnych list, ale zamiast tego po prostu dzielą się nimi z rodzicem. To znaczy, że program zajmie 16GB PAMIĘCI RAM (niezależnie od tego, ile podprocesów uruchomię) ze względu na podejście copy-on-write pod Linuksem? Czy ja poprawne czy brakuje mi czegoś, co mogłoby spowodować skopiowanie list?

EDIT : Nadal jestem zdezorientowany, po przeczytaniu trochę więcej na ten temat. Z jednej strony Linux używa funkcji copy-on-write, co powinno oznaczać, że żadne dane nie są kopiowane. Z drugiej strony, dostęp do obiektu zmieni jego liczbę ref-count (nadal Nie wiem dlaczego i co to oznacza). Mimo to, czy cały obiekt zostanie skopiowany?

Na przykład, jeśli zdefiniuję jakąś funkcję w następujący sposób:

def someFunction(list1, list2, list3):
    i=random.randint(0,99999)
    print list1[i], list2[i], list3[i]

Używałby funkcja ta oznacza, że l1, l2 i l3 będą kopiowane w całości dla każdego podprocesu?

Czy można to sprawdzić?

EDIT2 po przeczytaniu nieco więcej i monitorowaniu całkowitego wykorzystania pamięci systemu podczas uruchamiania podprocesów, wydaje się, że całe obiekty są rzeczywiście kopiowane dla każdego podprocesu. I wydaje się, że to dlatego, że liczenie referencji.

Liczenie referencji dla l1, l2 i l3 jest w moim programie zbędne. Dzieje się tak dlatego, że l1, l2 i l3 zostanie zachowana w pamięci (bez zmian) do momentu zakończenia procesu nadrzędnego. Do tego czasu nie ma potrzeby zwalniania pamięci używanej przez te listy. W zasadzie wiem na pewno, że liczba referencji pozostanie powyżej 0 (dla tych list i każdego obiektu z tych list) dopóki program się nie zakończy.

Więc teraz pojawia się pytanie, Jak mogę się upewnić, że obiekty nie zostaną skopiowane do każdego pod-procesu? Czy Mogę wyłączyć zliczanie referencji dla tych list i każdego obiektu w tych listy?

EDIT3 tylko dodatkowa uwaga. Podprocesy nie muszą modyfikować l1, l2 i l3 lub dowolne obiekty z tych list. Podprocesy muszą tylko być w stanie odwołać się do niektórych z tych obiektów bez powodowania kopiowania pamięci dla każdego podprocesu.

Author: Community, 2013-01-02

3 answers

Ogólnie rzecz biorąc, istnieją dwa sposoby udostępniania tych samych danych:

  • wielowątkowość
  • pamięć Dzielona

Wielowątkowość Pythona nie nadaje się do zadań związanych z procesorem (ze względu na Gil), więc zwyczajowym rozwiązaniem w tym przypadku jest kontynuowanie multiprocessing. Jednak dzięki temu rozwiązaniu musisz jawnie udostępniać dane, używając multiprocessing.Value oraz multiprocessing.Array.

Zauważ, że zazwyczaj dzielenie się danymi między procesami może nie być najlepszym wyborem, ze względu na wszystkie problemy z synchronizacją; podejście obejmujące aktorów wymieniających wiadomości jest zwykle postrzegane jako lepszy wybór. Zobacz także dokumentacja Pythona :

Jak wspomniano powyżej, podczas programowania współbieżnego Zwykle najlepiej unikać używania współdzielonego stanu w miarę możliwości. To jest szczególnie prawdziwe podczas korzystania z wielu procesów.

Jednakże, jeśli naprawdę potrzebujesz użyć niektórych udostępnionych danych, to multiprocessing zapewnia kilka sposobów na więc.

W Twoim przypadku, musisz zawinąć l1, l2 i l3 w jakiś sposób zrozumiałe przez multiprocessing (np. przez użycie multiprocessing.Array), a następnie przekazać je jako parametry.
Zauważ również, że, jak powiedziałeś, nie potrzebujesz dostępu do zapisu, wtedy powinieneś przekazać lock=False podczas tworzenia obiektów, w przeciwnym razie cały dostęp będzie nadal serializowany.

 38
Author: Roberto Liffredo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-04 07:48:52

Jeśli chcesz skorzystać z funkcji Kopiuj przy zapisie, a Twoje dane są statyczne ( niezmienione w procesach potomnych) - powinieneś sprawić, aby python nie mieszał się z blokami pamięci, w których leżą Twoje dane. Możesz to łatwo zrobić, używając struktur C lub c++ (na przykład stl) jako kontenerów i dostarczając własne wrappery Pythona, które będą używać wskaźników do pamięci danych (lub ewentualnie kopiować dane mem), gdy obiekt na poziomie Pythona zostanie utworzony, jeśli w ogóle. Wszystko to można zrobić bardzo łatwo z niemal prostotą i składnia z cython .

# pseudo cython
cdef class FooContainer:
   cdef char * data
   def __cinit__(self, char * foo_value):
       self.data = malloc(1024, sizeof(char))
       memcpy(self.data, foo_value, min(1024, len(foo_value)))

   def get(self):
       return self.data

# python part
from foo import FooContainer

f = FooContainer("hello world")
pid = fork()
if not pid:
   f.get() # this call will read same memory page to where
           # parent process wrote 1024 chars of self.data
           # and cython will automatically create a new python string
           # object from it and return to caller

Powyższy pseudo-kod jest źle napisany. Nie używaj go. W miejsce siebie.dane powinny być w Twoim przypadku kontenerem C lub c++.

 10
Author: Turnaev Evgeny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-03 19:41:34

Możesz użyć memcached lub redis i ustawić każdy jako parę wartości klucza {"l1",},..

 1
Author: CrabbyPete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-01 11:58:47