Strategie I / O dla problemów obliczeniowych z dużymi zestawami danych?

15

Moja grupa badawcza koncentruje się na dynamice molekularnej, która oczywiście może generować gigabajty danych w ramach jednej trajektorii, którą należy następnie przeanalizować.

Kilka problemów, którymi się zajmujemy, wiąże się z korelacjami w zbiorze danych, co oznacza, że ​​musimy śledzić duże ilości danych w pamięci i analizować je, zamiast stosować podejście bardziej sekwencyjne.

Chciałbym wiedzieć, jakie są najbardziej wydajne strategie obsługi operacji we / wy dużych zestawów danych w skryptach. Zwykle używamy skryptów opartych na języku Python, ponieważ powoduje to, że kodowanie pliku we / wy jest mniej bolesne niż C lub Fortran, ale kiedy mamy dziesiątki lub setki milionów linii, które należy przetworzyć, nie jest tak jasne, jakie jest najlepsze podejście . Czy powinniśmy rozważyć wprowadzenie kodu wejściowego do pliku w C, czy też inna strategia jest bardziej przydatna? (Czy po prostu wstępne załadowanie całej tablicy do pamięci będzie lepsze niż seria kolejnych odczytów „fragmentów” (rzędu megabajtów)?

Kilka dodatkowych uwag:

  • Szukamy przede wszystkim narzędzi skryptowych do post-processingu, a nie narzędzi „on-line” - stąd użycie Pythona.

  • re=16limΔt(x(t+Δt)-x(t))2)
eeismail
źródło

Odpowiedzi:

6

Zakładam, że twoje pytanie pochodzi z obserwacji, że I / O powoduje znaczny narzut w całej twojej analizie. W takim przypadku możesz spróbować pokryć operacje we / wy obliczeniami.

Udane podejście zależy od tego, jak uzyskujesz dostęp do danych i obliczeń, które wykonujesz na tych danych. Jeśli potrafisz zidentyfikować wzór lub dostęp do różnych regionów danych jest wcześniej znany, możesz spróbować pobrać „kolejne fragmenty” danych w tle podczas przetwarzania „bieżących fragmentów”.

Jako prosty przykład, jeśli przejdziesz plik tylko raz i przetworzysz każdą linię lub zestaw linii, możesz podzielić strumień na kawałki linii (lub MB). Następnie, przy każdej iteracji nad porcjami, możesz załadować porcję i + 1 podczas przetwarzania porcji i.

Twoja sytuacja może być bardziej złożona i wymagać bardziej zaangażowanych rozwiązań. W każdym razie chodzi o to, aby wykonać operacje wejścia / wyjścia w tle, podczas gdy procesor ma dane do pracy. Jeśli podasz więcej szczegółów na temat konkretnego problemu, możemy być w stanie przyjrzeć się mu głębiej;)

---- Wersja rozszerzona po podaniu więcej szczegółów ----

Nie jestem pewien, czy rozumiem notację, ale, jak powiedziałeś, pomysł polega na interakcji typu „wszystko dla wszystkich”. Wspominasz również, że dane mogą zmieścić się w pamięci RAM. Następnie zacznę od pomiaru czasu załadowania wszystkich danych i czasu wykonania obliczeń. Teraz,

  • jeśli procent we / wy jest niski (niski, ponieważ u ciebie nie zależy na kosztach ogólnych, cokolwiek to jest: 0,5%, 2%, 5%, ...), po prostu zastosuj proste podejście: załaduj dane od razu i oblicz. Zaoszczędzisz czas na bardziej interesujące aspekty swoich badań.

  • jeśli nie możesz sobie pozwolić na koszty ogólne, możesz zajrzeć do sugestii Pedro. Pamiętaj, o czym wspomniał Aron Ahmadia, i przetestuj to, zanim przejdziesz do pełnej implementacji.

  • n2)n

    ładuj porcję 1 i porcję 2
    dla fragmentów i = 1 do n
        asynchronicznie ładuj porcję i + 1
        dla fragmentów w j = i + 1 do n
            asynchronicznie ładuj porcję j + 1
            oblicz z fragmentami i, j (* dla pierwszej iteracji są to wstępnie załadowane fragmenty 1 i 2 *)

Uwaga: jest to szybki i nieprzyzwoity pseudokod, należałoby dostosować wskaźniki.

Aby to zaimplementować, powszechnie stosuje się tak zwane podwójne buforowanie . Z grubsza mówiąc: podziel pamięć na dwa obszary robocze; podczas ładowania danych w tle do obszaru roboczego 1, procesor oblicza dane w obszarze roboczym 2. Przy każdej iteracji wymieniaj rolę.

Przepraszam, że nie mogę teraz znaleźć dobrego odniesienia.

[1] Out-of-core algorytm zawiera pewien mechanizm (efektywnie) radzenia sobie z danymi znajdującymi się na dysku. Są one nazywane poza rdzeniem, a nie w rdzeniu („w pamięci RAM”).

Diego
źródło
7

Wcześniej miałem do czynienia z podobnymi problemami, a moim ulubionym rozwiązaniem jest użycie we / wy mapowanych w pamięci , choć w C ...

Zasada tego jest dość prosta: zamiast otwierać plik i czytać z niego, ładujesz go bezpośrednio do pamięci i uzyskujesz do niego dostęp, jakby to była ogromna tablica. Sztuczka, która sprawia, że ​​jest wydajna, polega na tym, że system operacyjny nie ładuje pliku , po prostu traktuje go jak zamienioną pamięć, którą należy załadować. Po uzyskaniu dostępu do dowolnego bajtu w pliku strona pamięci dla tej części pliku jest zamieniana na pamięć. Jeśli nadal będziesz uzyskiwać dostęp do różnych części pliku, a pamięć się zapełni, rzadziej używane części zostaną zamienione z powrotem - automatycznie!

Szybkie wyszukiwanie w Google mówi mi, że jest to również dostępne dla Pythona: 16.7. mmap - obsługa plików mapowanych w pamięci , ale nie wiem wystarczająco dużo o Pythonie, aby stwierdzić, czy to naprawdę to samo.

Pedro
źródło
1
Po prostu upewnij się, że mierzysz i testujesz, zanim zaimplementujesz coś mmapw swoim głównym kodzie. Wiele nowoczesnych systemów operacyjnych zapewnia podobną wydajność między zwykłymi readi mniej skomplikowanymi. (Tak, mmap w Pythonie zapewnia przenośny interfejs map pamięci systemu Windows i UNIX).
Aron Ahmadia,
1

Być może możesz użyć Cython w sekcjach We / Wy pliku i przekonwertować tę część na kod C.

asmatic
źródło