Welche Tricks verwenden Benutzer, um den verfügbaren Speicher einer interaktiven R-Sitzung zu verwalten? Ich benutze die folgenden Funktionen [basierend auf Beiträgen von Petr Pikal und David Hinds in der R-Help-Liste von 2004], um die größten Objekte aufzulisten (und / oder zu sortieren) und gelegentlichrm()
einige davon. Aber bei weitem die effektivste Lösung war ... unter 64-Bit-Linux mit viel Speicher zu laufen.
Irgendwelche anderen netten Tricks, die Leute teilen wollen? Eine pro Post, bitte.
# 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
quelle
quelle
multilevelPSA
Paket gepackt . Das Paket ist für etwas anderes konzipiert, aber Sie können die Funktion von dort aus verwenden, ohne das Paket zu laden, indem Sie sagenrequireNamespace(multilevelPSA); multilevelPSA::lsos(...)
. Oder imDmisc
Paket (nicht auf CRAN).Antworten:
Stellen Sie sicher, dass Sie Ihre Arbeit in einem reproduzierbaren Skript aufzeichnen. Öffnen Sie von Zeit zu Zeit R und dann
source()
Ihr Skript erneut. Sie bereinigen alles, was Sie nicht mehr verwenden, und haben als zusätzlichen Vorteil Ihren Code getestet.quelle
1-load.r
,2-explore.r
,3-model.r
- auf diese Weise an anderen offensichtlich ist , dass es eine gewisse Ordnung vorhanden.Ich benutze das Paket data.table . Mit seinem
:=
Operator können Sie:Keine dieser Operationen kopiert die (möglicherweise große)
data.table
überhaupt, nicht einmal.data.table
viel weniger Arbeitsspeicher benötigt wird.Verwandte Links :
:=
Operator in data.table verwenden?quelle
Hab das auf einem Twitter-Post gesehen und finde, es ist eine großartige Funktion von Dirk! In Anlehnung an die Antwort von JD Long würde ich dies zum benutzerfreundlichen Lesen tun:
Was zu so etwas führt:
HINWEIS: Der Hauptteil, den ich hinzugefügt habe, war (wiederum angepasst an JDs Antwort):
quelle
capture.output
keine Notwendigkeit mehr besteht undobj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })
eine saubere Ausgabe erzeugt. Wenn Sie es nicht entfernen, werden unerwünschte Anführungszeichen in der Ausgabe erzeugt, dh[1] "792.5 Mb"
anstelle von792.5 Mb
.obj.class <- napply(names, function(x) as.character(class(x))[1])
,obj.class <- napply(names, function(x) class(x)[1])
da ichclass
jetzt immer einen Vektor von Zeichen zurückgebe (base-3.5.0).Ich benutze den
subset
Parameter aggressiv, indem ich nur die erforderlichen Variablen auswähle, wenn ich Datenrahmen an dasdata=
Argument der Regressionsfunktionen übergebe. Es führt zwar zu einigen Fehlern, wenn ich vergesse, Variablen sowohl zur Formel als auch zumselect=
Vektor hinzuzufügen , spart jedoch aufgrund des verringerten Kopierens von Objekten viel Zeit und verringert den Speicherbedarf erheblich. Angenommen, ich habe 4 Millionen Datensätze mit 110 Variablen (und das tue ich auch). Beispiel:Zur Festlegung des Kontexts und der Strategie: Die
gdlab2
Variable ist ein logischer Vektor, der für Probanden in einem Datensatz erstellt wurde, der alle normalen oder fast normalen Werte für eine Reihe von Labortests aufwies, undHIVfinal
ein Zeichenvektor, der vorläufige und bestätigende Tests auf HIV zusammenfasste .quelle
Ich liebe Dirks .ls.objects () -Skript, aber ich blinzelte weiter, um die Zeichen in der Größenspalte zu zählen. Also habe ich ein paar hässliche Hacks gemacht, um es mit hübscher Formatierung für die Größe zu präsentieren:
quelle
Das ist ein guter Trick.
Ein weiterer Vorschlag ist, wo immer möglich speichereffiziente Objekte zu verwenden: Verwenden Sie beispielsweise eine Matrix anstelle eines data.frame.
Dies betrifft nicht wirklich die Speicherverwaltung, aber eine wichtige Funktion, die nicht allgemein bekannt ist, ist memory.limit (). Sie können die Standardeinstellung mit dem Befehl memory.limit (Größe = 2500) erhöhen, wobei die Größe in MB angegeben ist. Wie Dirk bereits erwähnt hat, müssen Sie 64-Bit verwenden, um dies wirklich nutzen zu können.
quelle
Mir gefällt die von Dirk entwickelte verbesserte Objektfunktion sehr gut. In den meisten Fällen reicht mir jedoch eine grundlegendere Ausgabe mit dem Objektnamen und der Größe aus. Hier ist eine einfachere Funktion mit einem ähnlichen Ziel. Die Speichernutzung kann alphabetisch oder nach Größe sortiert, auf eine bestimmte Anzahl von Objekten beschränkt und aufsteigend oder absteigend sortiert werden. Außerdem arbeite ich oft mit Daten, die 1 GB + groß sind, sodass die Funktion die Einheiten entsprechend ändert.
Und hier ist eine Beispielausgabe:
quelle
Ich speichere niemals einen R-Arbeitsbereich. Ich verwende Importskripte und Datenskripte und gebe besonders große Datenobjekte aus, die ich nicht oft in Dateien neu erstellen möchte. Auf diese Weise beginne ich immer mit einem neuen Arbeitsbereich und muss keine großen Objekte entfernen. Das ist aber eine sehr schöne Funktion.
quelle
Leider hatte ich keine Zeit, es ausgiebig zu testen, aber hier ist ein Gedächtnistipp, den ich vorher noch nicht gesehen habe. Für mich wurde der benötigte Speicher um mehr als 50% reduziert. Wenn Sie beispielsweise mit read.csv Inhalte in R einlesen, benötigen sie eine bestimmte Menge an Speicher. Danach können Sie sie mit speichern. Wenn
save("Destinationfile",list=ls())
Sie R das nächste Mal öffnen, können Sie sie verwenden.load("Destinationfile")
Jetzt hat die Speichernutzung möglicherweise abgenommen. Es wäre schön, wenn jemand bestätigen könnte, ob dies zu ähnlichen Ergebnissen mit einem anderen Datensatz führt.quelle
fread
und dann in .RData zu speichern. Die RData-Dateien waren zwar etwa 70% kleiner, aber nach dem erneuten Laden war der verwendete Speicher genau der gleiche. Hatte ich gehofft, dass dieser Trick den Speicherbedarf verringert? Vermisse ich etwas?Um die gängige Strategie häufiger Neustarts weiter zu veranschaulichen, können wir Littler verwenden , mit dem wir einfache Ausdrücke direkt über die Befehlszeile ausführen können. Hier ist ein Beispiel, mit dem ich manchmal verschiedene BLAS für ein einfaches Crossprod zeitlich festlege.
Gleichfalls,
Lädt das Matrix-Paket (über den Schalter --packages | -l) und führt die Beispiele der spMatrix-Funktion aus. Da r immer "frisch" startet, ist diese Methode auch ein guter Test während der Paketentwicklung.
Last but not least eignet sich r auch hervorragend für den automatisierten Batch-Modus in Skripten mit dem Shebang-Header '#! / Usr / bin / r'. Rscript ist eine Alternative, wenn weniger verfügbar ist (z. B. unter Windows).
quelle
Wenn Sie aus Gründen der Geschwindigkeit und des Speichers einen großen Datenrahmen über eine komplexe Reihe von Schritten erstellen, werde ich ihn regelmäßig (den in Bearbeitung befindlichen Datensatz, der gerade erstellt wird) auf die Festplatte leeren, an alle vorherigen Daten anhängen und ihn dann neu starten . Auf diese Weise arbeiten die Zwischenschritte nur bei kleineren Datenrahmen (was gut ist, da z. B. rbind bei größeren Objekten erheblich langsamer wird). Der gesamte Datensatz kann am Ende des Prozesses zurückgelesen werden, wenn alle Zwischenobjekte entfernt wurden.
quelle
Nur um zu beachten, dass
data.table
Paketetables()
ein ziemlich guter Ersatz für Dirks.ls.objects()
benutzerdefinierte Funktion zu sein scheinen (detailliert in früheren Antworten), obwohl nur für data.frames / Tabellen und nicht zB Matrizen, Arrays, Listen.quelle
Ich habe Glück und meine großen Datenmengen werden vom Instrument in "Chunks" (Teilmengen) von ungefähr 100 MB (32-Bit-Binärdatei) gespeichert. Auf diese Weise kann ich Vorverarbeitungsschritte (Löschen nicht informativer Teile, Downsampling) nacheinander ausführen, bevor der Datensatz zusammengeführt wird.
Das Aufrufen von
gc ()
"von Hand" kann hilfreich sein, wenn die Größe der Daten dem verfügbaren Speicher nahe kommt.Manchmal benötigt ein anderer Algorithmus viel weniger Speicher.
Manchmal gibt es einen Kompromiss zwischen Vektorisierung und Speichernutzung.
Vergleiche:
split
&lapply
gegen einefor
Schleife.Um eine schnelle und einfache Datenanalyse zu ermöglichen, arbeite ich häufig zuerst mit einer kleinen zufälligen Teilmenge (
sample ()
) der Daten. Sobald das Datenanalyseskript / .Rnw fertig ist, werden die Datenanalysecodes und die vollständigen Daten zur Berechnung über Nacht / über Wochenende / ... an den Berechnungsserver gesendet.quelle
Die Verwendung von Umgebungen anstelle von Listen zur Verarbeitung von Sammlungen von Objekten, die einen erheblichen Arbeitsspeicher belegen.
Der Grund: Jedes Mal, wenn ein Element einer
list
Struktur geändert wird, wird die gesamte Liste vorübergehend dupliziert. Dies wird zu einem Problem, wenn der Speicherbedarf der Liste etwa die Hälfte des verfügbaren Arbeitsspeichers beträgt, da dann Daten auf die langsame Festplatte übertragen werden müssen. Umgebungen hingegen unterliegen diesem Verhalten nicht und können ähnlich wie Listen behandelt werden.Hier ist ein Beispiel:
In Verbindung mit Strukturen wie
big.matrix
oder,data.table
die es ermöglichen, ihren Inhalt an Ort und Stelle zu ändern, kann eine sehr effiziente Speichernutzung erreicht werden.quelle
Die
ll
Funktion imgData
Paket kann auch die Speichernutzung jedes Objekts anzeigen.quelle
Wenn Sie die Lecks wirklich vermeiden möchten, sollten Sie vermeiden, große Objekte in der globalen Umgebung zu erstellen.
Normalerweise habe ich eine Funktion, die den Job erledigt und zurückgibt
NULL
- alle Daten werden in dieser oder anderen aufgerufenen Funktionen gelesen und bearbeitet.quelle
Mit nur 4 GB RAM (unter Windows 10, also ungefähr 2 oder mehr realistisch 1 GB) musste ich bei der Zuweisung sehr vorsichtig sein.
Ich benutze fast ausschließlich data.table.
Mit der Funktion 'fread' können Sie Informationen beim Import nach Feldnamen unterteilen. Importieren Sie zunächst nur die Felder, die tatsächlich benötigt werden. Wenn Sie Base R Read verwenden, setzen Sie die falschen Spalten unmittelbar nach dem Import auf Null.
Wie 42- vorschlägt, werde ich, wo immer möglich, unmittelbar nach dem Importieren der Informationen innerhalb der Spalten eine Teilmenge erstellen.
Ich rm () häufig Objekte aus der Umgebung, sobald sie nicht mehr benötigt werden, z. B. in der nächsten Zeile, nachdem ich sie verwendet habe, um etwas anderes zu unterteilen, und rufe gc () auf.
'fread' und 'fwrite' aus data.table können im Vergleich zu Lese- und Schreibvorgängen der Basis R sehr schnell sein.
Wie kpierce8 vorschlägt, schreibe ich fast immer alles aus der Umgebung heraus und fresse es wieder ein, selbst wenn Tausende / Hunderttausende winziger Dateien durchkommen müssen. Dies hält nicht nur die Umgebung "sauber" und hält die Speicherzuweisung niedrig, sondern R neigt möglicherweise aufgrund des schwerwiegenden Mangels an verfügbarem RAM dazu, häufig auf meinem Computer abzustürzen. sehr häufig. Wenn die Informationen auf dem Laufwerk selbst gesichert werden, während der Code verschiedene Phasen durchläuft, muss ich nicht von vorne beginnen, wenn er abstürzt.
Ab 2017 laufen meiner Meinung nach die schnellsten SSDs über den M2-Port mit einigen GB pro Sekunde. Ich habe eine wirklich einfache 50 GB Kingston V300 (550 MB / s) SSD, die ich als primäre Festplatte verwende (mit Windows und R). Ich behalte alle Masseninformationen auf einem billigen 500-GB-WD-Plattenteller. Ich verschiebe die Datensätze auf die SSD, wenn ich anfange, daran zu arbeiten. Dies, kombiniert mit "Freading" und "Fwrite", hat alles super geklappt. Ich habe versucht, 'ff' zu verwenden, bevorzuge aber das erstere. 4K-Lese- / Schreibgeschwindigkeiten können jedoch zu Problemen führen. Das Sichern einer Viertelmillion 1k-Dateien (im Wert von 250 MB) von der SSD auf dem Plattenteller kann Stunden dauern. Soweit mir bekannt ist, ist noch kein R-Paket verfügbar, mit dem der Chunkifizierungsprozess automatisch optimiert werden kann. Schauen Sie sich beispielsweise an, wie viel RAM ein Benutzer hat. Testen Sie die Lese- / Schreibgeschwindigkeiten des RAM / aller angeschlossenen Laufwerke und schlagen Sie dann ein optimales Chunkification-Protokoll vor. Dies könnte zu erheblichen Workflow-Verbesserungen / Ressourcenoptimierungen führen. zB teilen Sie es auf ... MB für den RAM -> teilen Sie es auf ... MB für die SSD -> teilen Sie es auf ... MB auf dem Plattenteller -> teilen Sie es auf ... MB auf dem Band. Es könnte Datensätze im Voraus abtasten, um einen realistischeren Messstab für die Arbeit zu erhalten.
Viele der Probleme, an denen ich in R gearbeitet habe, betreffen die Bildung von Kombinations- und Permutationspaaren, Tripeln usw., was das begrenzte RAM nur zu einer größeren Einschränkung macht, da sie sich häufig irgendwann zumindest exponentiell ausdehnen. Dies hat mich dazu gebracht, viel Aufmerksamkeit auf die Qualität im Gegensatz zur Quantität der Informationen zu richten, die zunächst in sie eingehen, anstatt zu versuchen, sie anschließend zu bereinigen, und auf die Abfolge der Vorgänge bei der Vorbereitung der Informationen (beginnend mit) die einfachste Operation und Erhöhung der Komplexität); zB Teilmenge, dann Zusammenführen / Verbinden, dann Kombinationen / Permutationen bilden usw.
In einigen Fällen scheint die Verwendung von Lesen und Schreiben auf Basis R einige Vorteile zu haben. Zum Beispiel ist die Fehlererkennung in 'fread' so gut, dass es schwierig sein kann, wirklich unordentliche Informationen in R zu bekommen, um sie zu bereinigen. Base R scheint auch viel einfacher zu sein, wenn Sie Linux verwenden. Base R scheint unter Linux gut zu funktionieren, Windows 10 benötigt ~ 20 GB Speicherplatz, während Ubuntu nur wenige GB benötigt, der mit Ubuntu benötigte RAM ist etwas niedriger. Bei der Installation von Paketen von Drittanbietern in (L) Ubuntu sind mir jedoch zahlreiche Warnungen und Fehler aufgefallen. Ich würde nicht empfehlen, sich zu weit von (L) Ubuntu oder anderen Aktiendistributionen mit Linux zu entfernen, da Sie so viel Gesamtkompatibilität verlieren können, dass der Prozess fast sinnlos wird (ich denke, dass 'Unity' in Ubuntu ab 2017 abgebrochen werden soll ).
Hoffentlich hilft einiges davon anderen.
quelle
Dies fügt dem oben Gesagten nichts hinzu, ist aber in dem einfachen und stark kommentierten Stil geschrieben, den ich mag. Es wird eine Tabelle mit den in der Größe geordneten Objekten angezeigt, jedoch ohne einige der in den obigen Beispielen angegebenen Details:
quelle
Dies ist eine neuere Antwort auf diese ausgezeichnete alte Frage. Aus Hadleys Advanced R:
( http://adv-r.had.co.nz/memory.html )
quelle
Wenn Sie unter Linux arbeiten und mehrere Prozesse verwenden möchten und nur Lesevorgänge für ein oder mehrere große Objekte ausführen müssen, verwenden Sie
makeForkCluster
anstelle von amakePSOCKcluster
. Dies spart Ihnen auch Zeit beim Senden des großen Objekts an die anderen Prozesse.quelle
Ich schätze einige der obigen Antworten sehr, nachdem ich @hadley und @Dirk gefolgt bin, die vorschlagen, R zu schließen
source
und die Befehlszeile auszugeben und zu verwenden. Ich habe eine Lösung gefunden, die für mich sehr gut funktioniert hat. Ich musste mich mit Hunderten von Massenspektren befassen, von denen jedes etwa 20 MB Speicher belegt, also habe ich zwei R-Skripte wie folgt verwendet:Zuerst ein Wrapper:
Mit diesem Skript steuere ich grundsätzlich, was mein Hauptskript tut
runConsensus.r
, und schreibe die Datenantwort für die Ausgabe. Damit scheint jedes Mal, wenn der Wrapper das Skript aufruft, das R erneut geöffnet und der Speicher freigegeben zu werden.Ich hoffe es hilft.
quelle
Neben den allgemeineren Speicherverwaltungstechniken in den obigen Antworten versuche ich immer, die Größe meiner Objekte so weit wie möglich zu reduzieren. Zum Beispiel arbeite ich mit sehr großen, aber sehr spärlichen Matrizen, mit anderen Worten Matrizen, bei denen die meisten Werte Null sind. Mit dem 'Matrix'-Paket (Großschreibung wichtig) konnte ich meine durchschnittliche Objektgröße von ~ 2 GB auf ~ 200 MB reduzieren, so einfach wie:
Das Matrix-Paket enthält Datenformate, die genau wie eine reguläre Matrix verwendet werden können (Sie müssen Ihren anderen Code nicht ändern), aber spärliche Daten viel effizienter speichern können, unabhängig davon, ob sie in den Speicher geladen oder auf der Festplatte gespeichert werden.
Außerdem haben die Rohdateien, die ich erhalte, ein "langes" Format, in dem jeder Datenpunkt Variablen enthält
x, y, z, i
. Viel effizienter, um die Daten in einx * y * z
Dimensionsarray mit nur Variablen umzuwandelni
.Kennen Sie Ihre Daten und verwenden Sie ein wenig gesunden Menschenverstand.
quelle
Tipp für den Umgang mit Objekten , für die eine umfangreiche Zwischenberechnung erforderlich ist : Wenn Sie Objekte verwenden, für deren Erstellung viele umfangreiche Berechnungen und Zwischenschritte erforderlich sind, ist es häufig hilfreich, einen Codeabschnitt mit der Funktion zum Erstellen des Objekts und anschließend einen separaten Block zu schreiben Code, mit dem ich das Objekt entweder generieren und als
rmd
Datei speichern oder extern aus einerrmd
Datei laden kann, die ich bereits zuvor gespeichert habe. Dies ist besonders einfach, wenn SieR Markdown
die folgende Code-Chunk-Struktur verwenden.Bei dieser Codestruktur muss ich nur ändern,
LOAD
je nachdem, ob ich das Objekt generieren und speichern oder direkt aus einer vorhandenen gespeicherten Datei laden möchte. (Natürlich muss ich es generieren und beim ersten Mal speichern, aber danach habe ich die Möglichkeit, es zu laden.) Das EinstellenLOAD = TRUE
umgeht die Verwendung meiner komplizierten Funktion und vermeidet alle darin enthaltenen umfangreichen Berechnungen. Diese Methode benötigt immer noch genügend Speicher, um das interessierende Objekt zu speichern, erspart Ihnen jedoch die Berechnung jedes Mal, wenn Sie Ihren Code ausführen. Bei Objekten, die eine umfangreiche Berechnung von Zwischenschritten erfordern (z. B. bei Berechnungen mit Schleifen über große Arrays), kann dies einen erheblichen Zeit- und Rechenaufwand bedeuten.quelle
Laufen
von Zeit zu Zeit hilft R auch dabei, unbenutzten, aber immer noch nicht freigegebenen Speicher freizugeben.
quelle
for
Schleife hier? Es gibt keineni
imgc
Aufruf.gc(reset = T)
neunmalige Kopieren und Einfügen zu vermeidenSie können auch einige Vorteile erzielen, indem Sie knitr verwenden und Ihr Skript in Rmd-Chuncks einfügen.
Normalerweise teile ich den Code in verschiedene Abschnitte auf und wähle aus, welcher einen Prüfpunkt im Cache oder in einer RDS-Datei speichern soll, und
Dort können Sie einen Block festlegen, der im "Cache" gespeichert werden soll, oder Sie können entscheiden, ob ein bestimmter Block ausgeführt werden soll oder nicht. Auf diese Weise können Sie in einem ersten Durchlauf nur "Teil 1" verarbeiten, in einer anderen Ausführung nur "Teil 2" usw. auswählen.
Beispiel:
Als Nebeneffekt könnte dies Ihnen auch Kopfschmerzen in Bezug auf die Reproduzierbarkeit ersparen :)
quelle
Basierend auf der Antwort von @ Dirk und @ Tony habe ich ein kleines Update gemacht. Das Ergebnis wurde
[1]
vor den hübschen Größenwerten ausgegeben , also habe ich das herausgenommen,capture.output
was das Problem gelöst hat:quelle
Ich versuche, die Anzahl der Objekte klein zu halten, wenn ich in einem größeren Projekt mit vielen Zwischenschritten arbeite. Also anstatt viele einzigartige Objekte zu erstellen, die aufgerufen werden
dataframe
->step1
->step2
->step3
->result
raster
->multipliedRast
->meanRastF
->sqrtRast
->resultRast
Ich arbeite mit temporären Objekten, die ich anrufe
temp
.dataframe
->temp
->temp
->temp
->result
Dadurch habe ich weniger Zwischendateien und mehr Übersicht.
Um mehr Speicherplatz zu sparen, kann ich ihn einfach entfernen,
temp
wenn er nicht mehr benötigt wird.Wenn ich mehrere Zwischendateien benötigen, verwende ich
temp1
,temp2
,temp3
.Zum Testen verwende ich
test
,test2
...quelle