Jakich sztuczek używają ludzie do zarządzania dostępną pamięcią interaktywnej sesji R. Korzystam z poniższych funkcji [na podstawie wpisów Petra Pikala i Davida Hindsa do listy pomocy w 2004 r.], Aby wyświetlić (i / lub posortować) największe obiekty oraz sporadycznie rm()
niektóre z nich. Jednak zdecydowanie najskuteczniejszym rozwiązaniem było ... uruchomienie pod 64-bitowym Linuksem z dużą ilością pamięci.
Jakieś inne fajne sztuczki, którymi ludzie chcą się podzielić? Poproszę jeden na post.
# improved list of objects
.ls.objects <- function (pos = 1, pattern, order.by,
decreasing=FALSE, head=FALSE, n=5) {
napply <- function(names, fn) sapply(names, function(x)
fn(get(x, pos = pos)))
names <- ls(pos = pos, pattern = pattern)
obj.class <- napply(names, function(x) as.character(class(x))[1])
obj.mode <- napply(names, mode)
obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
obj.size <- napply(names, object.size)
obj.dim <- t(napply(names, function(x)
as.numeric(dim(x))[1:2]))
vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
obj.dim[vec, 1] <- napply(names, length)[vec]
out <- data.frame(obj.type, obj.size, obj.dim)
names(out) <- c("Type", "Size", "Rows", "Columns")
if (!missing(order.by))
out <- out[order(out[[order.by]], decreasing=decreasing), ]
if (head)
out <- head(out, n)
out
}
# shorthand
lsos <- function(..., n=10) {
.ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}
memory-management
r
Dirk Eddelbuettel
źródło
źródło
multilevelPSA
paczkę . Pakiet jest przeznaczony do czegoś innego, ale możesz użyć funkcji stamtąd bez ładowania pakietu, mówiącrequireNamespace(multilevelPSA); multilevelPSA::lsos(...)
. Lub wDmisc
pakiecie (nie w CRAN).Odpowiedzi:
Upewnij się, że nagrywasz swoją pracę w powtarzalnym skrypcie. Od czasu do czasu ponownie otwórz R, a następnie
source()
skrypt. Wyczyścisz wszystko, czego już nie używasz, a dodatkową korzyścią będzie przetestowanie kodu.źródło
1-load.r
,2-explore.r
,3-model.r
- w ten sposób, że to oczywiste dla innych, że istnieje jakiś obecny porządek.Korzystam z pakietu data.table . Za pomocą jego
:=
operatora możesz:Żadna z tych operacji nie kopiuje (potencjalnie dużych)
data.table
w ogóle, ani razu.data.table
zużywa znacznie mniej pamięci roboczej.Powiązane linki :
:=
operatora w data.table?źródło
Widziałem to na Twitterze i myślę, że to świetna funkcja Dirka! Kontynuując odpowiedź JD Long, zrobiłbym to dla przyjaznego czytania:
Co powoduje coś takiego:
UWAGA: Główną częścią, którą dodałem, było (ponownie, dostosowane z odpowiedzi JD):
źródło
capture.output
nie jest już konieczne iobj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })
zapewnia czysty wynik. W rzeczywistości nie usunięcie go powoduje powstanie niechcianych cudzysłowów na wyjściu, tj.[1] "792.5 Mb"
Zamiast792.5 Mb
.obj.class <- napply(names, function(x) as.character(class(x))[1])
na,obj.class <- napply(names, function(x) class(x)[1])
ponieważ terazclass
zawsze zwraca wektor znaków (base-3.5.0).Agresywnie używam
subset
parametru z wyborem tylko wymaganych zmiennych przy przekazywaniu ramek danych dodata=
argumentu funkcji regresji. Powoduje to pewne błędy, jeśli zapomnę dodać zmienne zarówno do formuły, jak i doselect=
wektora, ale nadal oszczędza dużo czasu ze względu na zmniejszone kopiowanie obiektów i znacznie zmniejsza powierzchnię pamięci. Powiedzmy, że mam 4 miliony rekordów ze 110 zmiennymi (i mam.) Przykład:Poprzez ustawienie kontekstu i strategii:
gdlab2
zmienna jest logicznym wektorem, który został skonstruowany dla pacjentów w zbiorze danych, który miał wszystkie normalne lub prawie normalne wartości dla szeregu testów laboratoryjnych iHIVfinal
był wektorem znaków, który podsumował wstępne i potwierdzające testy na HIV .źródło
Uwielbiam skrypt .ls.objects () Dirka, ale wciąż zezowałem, żeby policzyć znaki w kolumnie wielkości. Więc zrobiłem kilka brzydkich hacków, aby zaprezentować się z ładnym formatowaniem dla rozmiaru:
źródło
To dobra sztuczka.
Inną sugestią jest użycie obiektów, które są wydajne pod względem pamięci, tam gdzie to możliwe: na przykład użyj matrycy zamiast data.frame.
To tak naprawdę nie odnosi się do zarządzania pamięcią, ale jedną ważną funkcją, która nie jest powszechnie znana, jest memory.limit (). Możesz zwiększyć wartość domyślną za pomocą tego polecenia memory.limit (rozmiar = 2500), gdzie rozmiar jest w MB. Jak wspomniał Dirk, musisz korzystać z wersji 64-bitowej, aby naprawdę z tego skorzystać.
źródło
Bardzo podoba mi się ulepszona funkcja obiektów opracowana przez Dirka. Przez większość czasu wystarcza mi bardziej podstawowe wyjście z nazwą i rozmiarem obiektu. Oto prostsza funkcja z podobnym celem. Wykorzystanie pamięci można uporządkować alfabetycznie lub według wielkości, może być ograniczone do określonej liczby obiektów i może być uporządkowane rosnąco lub malejąco. Poza tym często pracuję z danymi o wielkości 1 GB +, więc funkcja odpowiednio zmienia jednostki.
A oto przykładowe dane wyjściowe:
źródło
Nigdy nie zapisuję obszaru roboczego R. Używam skryptów importu i skryptów danych i wysyłam do szczególnie dużych obiektów danych, których nie chcę często odtwarzać. W ten sposób zawsze zaczynam od świeżego obszaru roboczego i nie muszę czyścić dużych obiektów. To jednak bardzo fajna funkcja.
źródło
Niestety nie zdążyłem go dokładnie przetestować, ale oto wskazówka dotycząca pamięci, której wcześniej nie widziałem. Dla mnie wymagana pamięć została zmniejszona o ponad 50%. Kiedy czytasz rzeczy do R np. Read.csv, wymagają one pewnej ilości pamięci. Następnie możesz zapisać je przy
save("Destinationfile",list=ls())
następnym otwarciu R, możesz użyćload("Destinationfile")
Teraz użycie pamięci mogło się zmniejszyć. Byłoby miło, gdyby ktoś mógł potwierdzić, czy daje to podobne wyniki z innym zestawem danych.źródło
fread
, a następnie zapisanych w .RData. Pliki RData były rzeczywiście o około 70% mniejsze, ale po ponownym załadowaniu wykorzystana pamięć była dokładnie taka sama. Miałem nadzieję, że ta sztuczka zmniejszy ślad pamięci ... coś mi brakuje?Aby dodatkowo zilustrować wspólną strategię częstych restartów, możemy użyć littler, który pozwala nam uruchamiać proste wyrażenia bezpośrednio z wiersza poleceń. Oto przykład, którego czasami używam do mierzenia czasu dla różnych BLAS dla prostej krzyżówki.
Również,
ładuje pakiet Matrix (za pomocą przełącznika --packages | -l) i uruchamia przykłady funkcji spMatrix. Ponieważ r zawsze zaczyna się od „świeżości”, ta metoda jest również dobrym testem podczas opracowywania pakietu.
Wreszcie, r również działa świetnie w trybie automatycznego wsadowego w skryptach przy użyciu nagłówka shebang '#! / Usr / bin / r'. Rscript jest alternatywą, w której littler jest niedostępny (np. W systemie Windows).
źródło
Zarówno w przypadku szybkości, jak i pamięci, budując dużą ramkę danych za pomocą złożonej serii kroków, okresowo opróżnię ją (budowany zestaw danych w toku) na dysk, dołączając do wszystkiego, co było wcześniej, a następnie uruchom ją ponownie . W ten sposób pośrednie kroki działają tylko na małych ramkach danych (co jest dobre, ponieważ np. Rbind spowalnia znacznie przy większych obiektach). Cały zestaw danych można odczytać z powrotem pod koniec procesu, gdy wszystkie pośrednie obiekty zostaną usunięte.
źródło
Należy zauważyć, że
data.table
pakiettables()
wydaje się być całkiem dobrym zamiennikiem.ls.objects()
niestandardowej funkcji Dirka (szczegółowo opisanej we wcześniejszych odpowiedziach), chociaż tylko dla data.frames / tabel, a nie np. Macierzy, tablic, list.źródło
Mam szczęście i moje duże zestawy danych są zapisywane przez instrument w „kawałkach” (podzbiorach) o wielkości około 100 MB (32-bitowy plik binarny). W ten sposób mogę wykonać etapy przetwarzania wstępnego (usuwanie nieinformacyjnych części, próbkowanie w dół) sekwencyjnie przed stopieniem zestawu danych.
Wywołanie
gc ()
„ręcznie” może pomóc, jeśli rozmiar danych zbliży się do dostępnej pamięci.Czasami inny algorytm wymaga znacznie mniej pamięci.
Czasami występuje kompromis między wektoryzacją a wykorzystaniem pamięci.
porównaj:
split
ilapply
vs.for
pętla.Ze względu na szybką i łatwą analizę danych często najpierw pracuję z małym losowym podzbiorem (
sample ()
) danych. Po zakończeniu skryptu analizy danych / .Rnw kod analizy danych i kompletne dane są przesyłane do serwera obliczeniowego w celu wykonania obliczeń za noc / w weekend / ...źródło
Wykorzystanie środowisk zamiast list do obsługi kolekcji obiektów, które zajmują znaczną ilość pamięci roboczej.
Powód: za każdym razem, gdy element
list
struktury jest modyfikowany, cała lista jest tymczasowo powielana. Staje się to problemem, jeśli zapotrzebowanie na miejsce na liście wynosi około połowy dostępnej pamięci roboczej, ponieważ wtedy dane muszą zostać zamienione na wolny dysk twardy. Z drugiej strony środowiska nie podlegają temu zachowaniu i można je traktować podobnie jak listy.Oto przykład:
W połączeniu ze strukturami takimi jak
big.matrix
lub,data.table
które pozwalają na zmianę ich zawartości w miejscu, można osiągnąć bardzo wydajne wykorzystanie pamięci.źródło
ll
Funkcji wgData
pakiecie mogą wykazywać wykorzystania pamięci każdego obiektu, jak również.źródło
Jeśli naprawdę chcesz uniknąć wycieków, powinieneś unikać tworzenia dużych obiektów w środowisku globalnym.
To, co zwykle robię, to mieć funkcję, która wykonuje zadanie i zwraca
NULL
- wszystkie dane są odczytywane i przetwarzane w tej funkcji lub w innych wywoływanych przez nią funkcjach.źródło
Z tylko 4 GB pamięci RAM (z systemem Windows 10, więc upewnij się, że około 2 lub więcej realistycznie 1 GB) musiałem być bardzo ostrożny z alokacją.
Używam data.table prawie wyłącznie.
Funkcja „fread” pozwala na podzbiór informacji według nazw pól podczas importu; importuj tylko pola, które są naprawdę potrzebne na początek. Jeśli używasz odczytu R, zeruj fałszywe kolumny natychmiast po zaimportowaniu.
Jak sugeruje 42 , tam, gdzie to możliwe, podzbiorę w kolumnach natychmiast po zaimportowaniu informacji.
Często rm () obiekty ze środowiska, gdy tylko nie są już potrzebne, np. W następnym wierszu po użyciu ich do podzbioru czegoś innego i wywołanie gc ().
„fread” i „fwrite” z data.table mogą być bardzo szybkie w porównaniu z bazowymi odczytami i odczytami R.
Jak sugeruje kpierce8 , prawie zawsze piszę wszystko ze środowiska i przeglądam je z powrotem, nawet z tysiącami / setkami tysięcy małych plików do przejścia. To nie tylko utrzymuje czystość środowiska i utrzymuje niski przydział pamięci, ale prawdopodobnie z powodu poważnego braku dostępnej pamięci RAM R ma skłonność do częstych awarii na moim komputerze; bardzo często. Tworzenie kopii zapasowej informacji na samym dysku, gdy kod przechodzi przez różne etapy, oznacza, że nie muszę zaczynać od samego początku, jeśli ulegnie awarii.
Według stanu na 2017 r. Najszybsze dyski SSD pracują z prędkością około kilku GB na sekundę przez port M2. Mam naprawdę podstawowy dysk SSD Kingston V300 o pojemności 50 GB (550 MB / s), którego używam jako mojego głównego dysku (na nim jest Windows i R). Przechowuję wszystkie informacje masowe na tanim talerzu WD o pojemności 500 GB. Przenoszę zestawy danych na dysk SSD, kiedy zaczynam nad nimi pracować. To, w połączeniu z „fread” i „fwrite”, wszystko działało świetnie. Próbowałem użyć „ff”, ale wolę ten pierwszy. Prędkość odczytu / zapisu 4K może jednak powodować problemy; tworzenie kopii zapasowej ćwierć miliona plików 1k (o wartości 250 MB) z dysku SSD na talerz może zająć wiele godzin. O ile mi wiadomo, nie ma jeszcze dostępnego pakietu R, który mógłby automatycznie zoptymalizować proces „chunkification”; np. sprawdź, ile pamięci RAM ma użytkownik, przetestuj prędkości odczytu / zapisu pamięci RAM / wszystkich podłączonych napędów, a następnie zaproponuj optymalny protokół „chunkification”. Może to spowodować znaczące ulepszenia przepływu pracy / optymalizację zasobów; np. podziel go na ... MB dla pamięci RAM -> podziel na ... MB na dysk SSD -> podziel na ... MB na talerzu -> podziel na ... MB na taśmie. Może wcześniej próbkować zestawy danych, aby nadać mu bardziej realistyczny wskaźnik do pracy.
Wiele problemów, nad którymi pracowałem w R, polega na tworzeniu par kombinacji i permutacji, potrójnych itd., Co sprawia, że posiadanie ograniczonej pamięci RAM jest bardziej ograniczeniem, ponieważ często będą one przynajmniej wykładniczo rozszerzane w pewnym momencie. To sprawiło, że skupiłem dużo uwagi na jakości, a nie na ilości informacji, które trafiają do nich na początku, zamiast próbować je później wyczyścić, a także na sekwencji operacji przygotowujących informacje na początek (zaczynając od najprostsza operacja i zwiększenie złożoności); np. podzbiór, następnie scalanie / łączenie, a następnie tworzenie kombinacji / permutacji itp.
Wydaje się, że korzystanie z podstawowego odczytu i zapisu w niektórych przypadkach przynosi pewne korzyści. Na przykład wykrywanie błędów w ramach „fread” jest tak dobre, że próba wprowadzenia naprawdę niechlujnych informacji do R na początek może być trudna. Baza R wydaje się również znacznie łatwiejsza, jeśli używasz Linuksa. Baza R wydaje się działać dobrze w systemie Linux, Windows 10 zużywa ~ 20 GB miejsca na dysku, podczas gdy Ubuntu potrzebuje tylko kilku GB, pamięć RAM potrzebna dla Ubuntu jest nieco niższa. Ale zauważyłem duże ilości ostrzeżeń i błędów podczas instalowania pakietów stron trzecich w (L) Ubuntu. Nie polecałbym odchodzenia zbyt daleko od (L) Ubuntu lub innych dystrybucji akcji z Linuksem, ponieważ możesz stracić tyle ogólnej kompatybilności, że czyni to proces prawie bezcelowym (myślę, że „jedność” ma zostać anulowana w Ubuntu od 2017 roku ).
Mam nadzieję, że niektóre z nich mogą pomóc innym.
źródło
To nic nie dodaje do powyższego, ale jest napisane w prostym i mocno komentowanym stylu, który lubię. Daje tabelę z obiektami uporządkowanymi pod względem wielkości, ale bez niektórych szczegółów podanych w powyższych przykładach:
źródło
To jest nowa odpowiedź na to wspaniałe stare pytanie. Z Hadley's Advanced R:
( http://adv-r.had.co.nz/memory.html )
źródło
Jeśli pracujesz na systemie Linux i chcą korzystać z kilku procesów i mają tylko zrobić odczytu operacji na jednym lub więcej dużych obiektów użyć
makeForkCluster
zamiastmakePSOCKcluster
. Oszczędza to również czas wysyłania dużego obiektu do innych procesów.źródło
Naprawdę doceniam niektóre z powyższych odpowiedzi, następujące po @hadley i @Dirk, które sugerują zamknięcie R i wydanie
source
i użycie wiersza poleceń, wpadłem na rozwiązanie, które działało bardzo dobrze dla mnie. Musiałem poradzić sobie z setkami widm masowych, z których każde zajmuje około 20 Mb pamięci, więc użyłem dwóch skryptów R w następujący sposób:Najpierw opakowanie:
za pomocą tego skryptu w zasadzie kontroluję to, co robi mój główny skrypt
runConsensus.r
, i piszę odpowiedź na dane wyjściowe. Dzięki temu za każdym razem, gdy otoki wywołuje skrypt, wydaje się, że R jest ponownie otwierane i pamięć jest zwalniana.Mam nadzieję, że to pomoże.
źródło
Oprócz bardziej ogólnych technik zarządzania pamięcią podanych w powyższych odpowiedziach, zawsze staram się jak najbardziej zmniejszyć rozmiar moich obiektów. Na przykład pracuję z bardzo dużymi, ale bardzo rzadkimi macierzami, innymi słowy macierzami, w których większość wartości wynosi zero. Korzystając z pakietu „Matrix” (ważne wielkie litery) udało mi się zmniejszyć moje średnie rozmiary obiektów z ~ 2 GB do ~ 200 MB, tak jak:
Pakiet Matrix zawiera formaty danych, których można używać dokładnie tak jak zwykłą matrycę (nie trzeba zmieniać innego kodu), ale są one w stanie znacznie wydajniej przechowywać rzadkie dane, niezależnie od tego, czy są załadowane do pamięci, czy zapisane na dysku.
Dodatkowo, pliki surowe, które otrzymuję, mają „długi” format, w którym każdy punkt danych ma zmienne
x, y, z, i
. Znacznie bardziej wydajne przekształcanie danych wx * y * z
tablicę wymiarów z tylko zmiennąi
.Poznaj swoje dane i zachowaj zdrowy rozsądek.
źródło
Wskazówka dotycząca postępowania z obiektami wymagającymi ciężkich obliczeń pośrednich: Podczas tworzenia obiektów wymagających dużej ilości obliczeń i pośrednich kroków często przydaje mi się napisanie fragmentu kodu z funkcją utworzenia obiektu, a następnie oddzielnego fragmentu kodu, który daje mi opcję albo wygenerowania i zapisania obiektu jako
rmd
pliku, albo załadowania go zewnętrznie zrmd
pliku, który już wcześniej zapisałem. Jest to szczególnie łatwe wR Markdown
użyciu poniższej struktury fragmentu kodu.W tej strukturze kodu wszystko, co muszę zrobić, to zmienić w
LOAD
zależności od tego, czy chcę wygenerować i zapisać obiekt, czy załadować go bezpośrednio z istniejącego zapisanego pliku. (Oczywiście, muszę go wygenerować i zapisać za pierwszym razem, ale potem mam opcję załadowania go.) UstawienieLOAD = TRUE
pomija użycie mojej skomplikowanej funkcji i pozwala uniknąć wszystkich związanych z tym ciężkich obliczeń. Ta metoda wciąż wymaga wystarczającej ilości pamięci do przechowywania interesującego obiektu, ale oszczędza ci konieczności obliczania go za każdym razem, gdy uruchamiasz kod. W przypadku obiektów wymagających dużej ilości obliczeń pośrednich kroków (np. Do obliczeń obejmujących pętle na dużych tablicach) może to zaoszczędzić znaczną ilość czasu i obliczeń.źródło
Bieganie
od czasu do czasu pomaga również R uwolnić nieużywaną, ale wciąż nie zwolnioną pamięć.
źródło
for
tutaj pętla? Nie ma sięi
wgc
rozmowy.gc(reset = T)
dziewięć razyMożesz również uzyskać pewne korzyści, używając knitr i umieszczając swój skrypt w chmurze Rmd.
Zwykle dzielę kod na różne części i wybieram, który z nich zapisze punkt kontrolny w pamięci podręcznej lub w pliku RDS, a
Tam możesz ustawić, aby fragment był zapisywany w „pamięci podręcznej” lub możesz zdecydować, czy chcesz uruchomić określony fragment, czy nie. W ten sposób w pierwszym uruchomieniu możesz przetworzyć tylko „część 1”, w innym wykonaniu możesz wybrać tylko „część 2” itp.
Przykład:
Efektem ubocznym może być również oszczędność bólu głowy w zakresie powtarzalności :)
źródło
Na podstawie odpowiedzi @ Dirka i @ Tony'ego dokonałem niewielkiej aktualizacji. Wynik był generowany
[1]
przed ładnymi wartościami wielkości, więc wyjąłem ten,capture.output
który rozwiązał problem:źródło
Staram się utrzymywać małą liczbę obiektów podczas pracy w większym projekcie z wieloma pośrednimi krokami. Zamiast tworzyć wiele unikalnych obiektów o nazwie
dataframe
->step1
->step2
->step3
->result
raster
->multipliedRast
->meanRastF
->sqrtRast
->resultRast
Pracuję z tymczasowymi obiektami, które nazywam
temp
.dataframe
->temp
->temp
->temp
->result
Co pozostawia mi mniej pośrednich plików i więcej informacji.
Aby zaoszczędzić więcej pamięci, mogę po prostu usunąć,
temp
gdy nie jest już potrzebny.Jeśli muszę kilka plików pośrednich, używam
temp1
,temp2
,temp3
.Do testowania używam
test
,test2
...źródło