Quelles astuces les gens utilisent-ils pour gérer la mémoire disponible d'une session R interactive? J'utilise les fonctions ci-dessous [basées sur les publications de Petr Pikal et David Hinds sur la liste r-help en 2004] pour répertorier (et / ou trier) les plus gros objets et occasionnellement rm()
certains d'entre eux. Mais de loin la solution la plus efficace était ... de fonctionner sous Linux 64 bits avec une mémoire suffisante.
D'autres bons trucs que les gens veulent partager? Un par poste, s'il vous plaît.
# 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
la source
la source
multilevelPSA
paquet . Le package est conçu pour autre chose, mais vous pouvez utiliser la fonction à partir de là sans charger le package en disantrequireNamespace(multilevelPSA); multilevelPSA::lsos(...)
. Ou dans l'Dmisc
emballage (pas sur CRAN).Réponses:
Assurez-vous d'enregistrer votre travail dans un script reproductible. De temps en temps, rouvrez R, puis
source()
votre script. Vous nettoierez tout ce que vous n'utilisez plus et, comme avantage supplémentaire, vous aurez testé votre code.la source
1-load.r
,2-explore.r
,3-model.r
- de cette façon , il est évident aux autres qu'il ya un certain présent ordre.J'utilise le package data.table . Avec son
:=
opérateur, vous pouvez:Aucune de ces opérations ne copie le (potentiellement grand)
data.table
, pas même une seule fois.data.table
utilise beaucoup moins de mémoire de travail.Liens connexes :
:=
opérateur dans data.table?la source
J'ai vu ça sur un post Twitter et je pense que c'est une fonction géniale de Dirk! Dans le prolongement de la réponse de JD Long, je le ferais pour une lecture conviviale:
Ce qui se traduit par quelque chose comme ceci:
REMARQUE: La partie principale que j'ai ajoutée était (encore une fois, adaptée de la réponse de JD):
la source
capture.output
n'est plus nécessaire etobj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })
produit une sortie propre. En fait, ne pas le supprimer produit des guillemets indésirables dans la sortie, c'est-à-dire[1] "792.5 Mb"
au lieu de792.5 Mb
.obj.class <- napply(names, function(x) as.character(class(x))[1])
àobj.class <- napply(names, function(x) class(x)[1])
depuisclass
toujours revenir un vecteur de caractères maintenant (base-3.5.0).Je fais un usage agressif du
subset
paramètre en sélectionnant uniquement les variables requises lors du passage de trames de données à l'data=
argument des fonctions de régression. Cela entraîne des erreurs si j'oublie d'ajouter des variables à la fois à la formule et auselect=
vecteur, mais cela économise encore beaucoup de temps en raison d'une diminution de la copie d'objets et réduit considérablement l'empreinte mémoire. Disons que j'ai 4 millions d'enregistrements avec 110 variables (et je le fais.) Exemple:En définissant le contexte et la stratégie: la
gdlab2
variable est un vecteur logique qui a été construit pour des sujets dans un ensemble de données qui avait toutes les valeurs normales ou presque normales pour un tas de tests de laboratoire etHIVfinal
était un vecteur de caractères qui résumait les tests préliminaires et confirmatifs pour le VIH .la source
J'adore le script .ls.objects () de Dirk mais j'ai continué à plisser les yeux pour compter les caractères dans la colonne de taille. J'ai donc fait de vilains hacks pour le rendre présent avec un joli formatage pour la taille:
la source
Voilà une bonne astuce.
Une autre suggestion est d'utiliser autant que possible des objets économes en mémoire: par exemple, utilisez une matrice au lieu d'un data.frame.
Cela ne concerne pas vraiment la gestion de la mémoire, mais une fonction importante qui n'est pas largement connue est memory.limit (). Vous pouvez augmenter la valeur par défaut à l'aide de cette commande, memory.limit (taille = 2500), où la taille est en Mo. Comme Dirk l'a mentionné, vous devez utiliser 64 bits pour en tirer un réel avantage.
la source
J'aime bien la fonction d'objets améliorés développée par Dirk. La plupart du temps cependant, une sortie plus basique avec le nom et la taille de l'objet me suffit. Voici une fonction plus simple avec un objectif similaire. L'utilisation de la mémoire peut être ordonnée par ordre alphabétique ou par taille, peut être limitée à un certain nombre d'objets et peut être ordonnée ascendante ou descendante. De plus, je travaille souvent avec des données de 1 Go +, donc la fonction change d'unité en conséquence.
Et voici un exemple de sortie:
la source
Je ne sauvegarde jamais un espace de travail R. J'utilise des scripts d'importation et des scripts de données et je génère des objets de données particulièrement volumineux que je ne souhaite pas recréer souvent dans des fichiers. De cette façon, je commence toujours par un nouvel espace de travail et je n'ai pas besoin de nettoyer les gros objets. C'est une très belle fonction cependant.
la source
Malheureusement, je n'ai pas eu le temps de le tester de manière approfondie mais voici une astuce mémoire que je n'ai jamais vue auparavant. Pour moi, la mémoire requise a été réduite de plus de 50%. Lorsque vous lisez des éléments dans R avec par exemple read.csv, ils nécessitent une certaine quantité de mémoire. Après cela, vous pouvez les enregistrer avec
save("Destinationfile",list=ls())
La prochaine fois que vous ouvrirez R, vous pouvez utiliserload("Destinationfile")
Maintenant, l'utilisation de la mémoire peut avoir diminué. Ce serait bien si quelqu'un pouvait confirmer si cela produit des résultats similaires avec un ensemble de données différent.la source
fread
, puis enregistré sur .RData. Les fichiers RData étaient en effet environ 70% plus petits mais après le rechargement, la mémoire utilisée était exactement la même. J'espérais que cette astuce réduirait l'empreinte mémoire ... est-ce que je manque quelque chose?Pour illustrer davantage la stratégie commune de redémarrages fréquents, nous pouvons utiliser littler qui nous permet d'exécuter des expressions simples directement à partir de la ligne de commande. Voici un exemple que j'utilise parfois pour chronométrer différents BLAS pour un simple crossprod.
Également,
charge le package Matrix (via le commutateur --packages | -l) et exécute les exemples de la fonction spMatrix. Comme r démarre toujours «frais», cette méthode est également un bon test lors du développement d'un package.
Last but not least r fonctionne également très bien pour le mode batch automatisé dans les scripts utilisant l'en-tête de shebang '#! / Usr / bin / r'. Rscript est une alternative où littler n'est pas disponible (par exemple sous Windows).
la source
Pour des raisons de vitesse et de mémoire, lors de la création d'un grand bloc de données via une série d'étapes complexes, je le viderai périodiquement (l'ensemble de données en cours de construction) sur le disque, en l'ajoutant à tout ce qui précède, puis en le redémarrant . De cette façon, les étapes intermédiaires ne fonctionnent que sur des trames de données de petite taille (ce qui est bon car, par exemple, rbind ralentit considérablement avec des objets plus gros). L'ensemble des données peut être relu à la fin du processus, lorsque tous les objets intermédiaires ont été supprimés.
la source
Juste pour noter que les
data.table
paquetstables()
semblent être un assez bon remplacement pour la.ls.objects()
fonction personnalisée de Dirk (détaillée dans les réponses précédentes), bien que juste pour data.frames / tables et non par exemple des matrices, des tableaux, des listes.la source
J'ai de la chance et mes grands ensembles de données sont enregistrés par l'instrument en «morceaux» (sous-ensembles) d'environ 100 Mo (32 bits binaires). Ainsi, je peux effectuer des étapes de prétraitement (suppression de parties non informatives, sous-échantillonnage) séquentiellement avant de fusionner l'ensemble de données.
Appeler
gc ()
"à la main" peut aider si la taille des données se rapproche de la mémoire disponible.Parfois, un algorithme différent a besoin de beaucoup moins de mémoire.
Parfois, il y a un compromis entre la vectorisation et l'utilisation de la mémoire.
comparer:
split
&lapply
contre unefor
boucle.Pour une analyse rapide et facile des données, je travaille souvent d'abord avec un petit sous-ensemble aléatoire (
sample ()
) des données. Une fois que le script d'analyse de données / .Rnw est terminé, le code d'analyse de données et les données complètes sont envoyées au serveur de calcul pour le calcul pendant la nuit / le week-end / ....la source
L'utilisation d'environnements au lieu de listes pour gérer des collections d'objets qui occupent une quantité importante de mémoire de travail.
La raison: chaque fois qu'un élément d'une
list
structure est modifié, toute la liste est temporairement dupliquée. Cela devient un problème si l'exigence de stockage de la liste représente environ la moitié de la mémoire de travail disponible, car les données doivent alors être échangées sur le disque dur lent. Les environnements, en revanche, ne sont pas soumis à ce comportement et ils peuvent être traités de manière similaire aux listes.Voici un exemple:
En conjonction avec des structures telles que
big.matrix
oudata.table
qui permettent de modifier leur contenu sur place, une utilisation de la mémoire très efficace peut être obtenue.la source
La
ll
fonction dans legData
package peut également afficher l'utilisation de la mémoire de chaque objet.la source
Si vous voulez vraiment éviter les fuites, vous devez éviter de créer de gros objets dans l'environnement global.
Ce que je fais habituellement, c'est d'avoir une fonction qui fait le travail et renvoie
NULL
- toutes les données sont lues et manipulées dans cette fonction ou d'autres qu'elle appelle.la source
Avec seulement 4 Go de RAM (exécutant Windows 10, alors faites-en environ 2 ou plus de manière réaliste 1 Go), j'ai dû être très prudent avec l'allocation.
J'utilise data.table presque exclusivement.
La fonction «fread» vous permet de sous-définir les informations par nom de champ lors de l'importation; importer uniquement les champs réellement nécessaires au départ. Si vous utilisez la lecture de base R, annulez les colonnes parasites immédiatement après l'importation.
Comme le suggère 42- , dans la mesure du possible, je vais ensuite sous-ensemble dans les colonnes immédiatement après l'importation des informations.
Je rm () fréquemment des objets de l'environnement dès qu'ils ne sont plus nécessaires, par exemple sur la ligne suivante après les avoir utilisés pour sous-définir autre chose, et appeler gc ().
'fread' et 'fwrite' de data.table peuvent être très rapides en comparaison avec les lectures et écritures de base R.
Comme le suggère kpierce8 , je réécris presque toujours tout ce qui se trouve dans l'environnement et je le redoute, même avec des milliers / centaines de milliers de petits fichiers à passer. Cela permet non seulement de maintenir l'environnement «propre» et de maintenir l'allocation de mémoire faible, mais, peut-être en raison du grave manque de RAM disponible, R a tendance à se planter fréquemment sur mon ordinateur; très fréquemment. La sauvegarde des informations sur le disque lui-même au fur et à mesure que le code progresse à travers différentes étapes signifie que je n'ai pas à recommencer dès le début s'il se bloque.
En 2017, je pense que les SSD les plus rapides tournent autour de quelques Go par seconde via le port M2. J'ai un SSD Kingston V300 50 Go (550 Mo / s) vraiment basique que j'utilise comme disque principal (avec Windows et R dessus). Je garde toutes les informations en vrac sur un plateau WD bon marché de 500 Go. Je déplace les ensembles de données sur le SSD lorsque je commence à travailler dessus. Ceci, combiné avec tout ce qui a «effrayé» et «écrit», a très bien fonctionné. J'ai essayé d'utiliser 'ff' mais je préfère le premier. Les vitesses de lecture / écriture 4K peuvent créer des problèmes avec cela; La sauvegarde d'un quart de million de fichiers 1k (valeur de 250 Mo) du SSD sur le plateau peut prendre des heures. Pour autant que je sache, il n'y a pas encore de package R disponible qui puisse optimiser automatiquement le processus de «chunkification»; par exemple, regardez combien de RAM un utilisateur a, tester les vitesses de lecture / écriture de la RAM / de tous les disques connectés puis suggérer un protocole de «chunkification» optimal. Cela pourrait produire des améliorations significatives du flux de travail / optimisations des ressources; par exemple, divisez-le en ... Mo pour le bélier -> divisez-le en ... Mo pour le SSD -> divisez-le en ... Mo sur le plateau -> divisez-le en ... Mo sur la bande. Il pourrait échantillonner des ensembles de données à l'avance pour lui donner un bâton de mesure plus réaliste à partir duquel travailler.
Beaucoup de problèmes sur lesquels j'ai travaillé dans R impliquent la formation de combinaisons et de permutations, de triplets, etc., ce qui ne fait que limiter la RAM, car ils s'étendent souvent au moins de manière exponentielle à un moment donné. Cela m'a fait me concentrer beaucoup d'attention sur la qualité plutôt que sur la quantité d'informations qui y sont fournies au départ, plutôt que d'essayer de les nettoyer par la suite, et sur la séquence des opérations de préparation des informations pour commencer (en commençant par l'opération la plus simple et l'augmentation de la complexité); par exemple sous-ensemble, puis fusionner / joindre, puis former des combinaisons / permutations, etc.
Il semble y avoir certains avantages à utiliser la lecture et l'écriture de base R dans certains cas. Par exemple, la détection d'erreurs dans 'fread' est si bonne qu'il peut être difficile d'essayer d'obtenir des informations vraiment en désordre dans R pour commencer pour les nettoyer. Base R semble également être beaucoup plus facile si vous utilisez Linux. La base R semble bien fonctionner sous Linux, Windows 10 utilise environ 20 Go d'espace disque alors qu'Ubuntu n'a besoin que de quelques Go, la RAM nécessaire avec Ubuntu est légèrement inférieure. Mais j'ai remarqué de grandes quantités d'avertissements et d'erreurs lors de l'installation de packages tiers dans (L) Ubuntu. Je ne recommanderais pas de dériver trop loin de (L) Ubuntu ou d'autres distributions de stock avec Linux car vous pouvez perdre tellement de compatibilité globale que cela rend le processus presque inutile (je pense que `` l'unité '' doit être annulée dans Ubuntu à partir de 2017 ).
Avec un peu de chance, cela pourrait aider les autres.
la source
Cela n'ajoute rien à ce qui précède, mais est écrit dans le style simple et fortement commenté que j'aime. Il donne un tableau avec les objets classés en taille, mais sans certains détails donnés dans les exemples ci-dessus:
la source
Il s'agit d'une nouvelle réponse à cette excellente vieille question. De Advanced R de Hadley:
( http://adv-r.had.co.nz/memory.html )
la source
Si vous travaillez sur Linux et que vous souhaitez utiliser plusieurs processus et que vous n'avez qu'à effectuer des opérations de lecture sur un ou plusieurs objets volumineux, utilisez
makeForkCluster
au lieu de amakePSOCKcluster
. Cela vous fait également gagner du temps lors de l'envoi du gros objet aux autres processus.la source
J'apprécie vraiment certaines des réponses ci-dessus, après @hadley et @Dirk qui suggèrent de fermer R et d'émettre
source
et d'utiliser la ligne de commande, je trouve une solution qui a très bien fonctionné pour moi. J'ai dû gérer des centaines de spectres de masse, chacun occupant environ 20 Mo de mémoire, j'ai donc utilisé deux scripts R, comme suit:Tout d'abord un emballage:
avec ce script, je contrôle essentiellement ce que fait mon script principal
runConsensus.r
et j'écris la réponse des données pour la sortie. Avec cela, chaque fois que le wrapper appelle le script, il semble que le R soit rouvert et que la mémoire soit libérée.J'espère que cela aide.
la source
En plus des techniques de gestion de la mémoire plus générales données dans les réponses ci-dessus, j'essaie toujours de réduire autant que possible la taille de mes objets. Par exemple, je travaille avec des matrices très grandes mais très clairsemées, c'est-à-dire des matrices où la plupart des valeurs sont nulles. En utilisant le package «Matrix» (capitalisation importante), j'ai pu réduire la taille moyenne de mes objets de ~ 2 Go à ~ 200 Mo aussi simplement que:
Le package Matrix comprend des formats de données qui peuvent être utilisés exactement comme une matrice régulière (pas besoin de changer votre autre code) mais sont capables de stocker des données éparses beaucoup plus efficacement, qu'elles soient chargées en mémoire ou enregistrées sur disque.
De plus, les fichiers bruts que je reçois sont au format «long» où chaque point de données a des variables
x, y, z, i
. Beaucoup plus efficace pour transformer les données en unx * y * z
tableau de dimensions avec uniquement une variablei
.Connaissez vos données et utilisez un peu de bon sens.
la source
Astuce pour traiter des objets nécessitant un calcul intermédiaire lourd: Lorsque vous utilisez des objets qui nécessitent beaucoup de calculs lourds et d'étapes intermédiaires pour créer, je trouve souvent utile d'écrire un morceau de code avec la fonction pour créer l'objet, puis un morceau séparé de code qui me donne la possibilité de générer et d'enregistrer l'objet en tant que
rmd
fichier, ou de le charger en externe à partir d'unrmd
fichier que j'ai déjà enregistré précédemment. Ceci est particulièrement facile à faire enR Markdown
utilisant la structure de blocs de code suivante.Avec cette structure de code, tout ce que je dois faire est de changer
LOAD
selon que je veux générer et enregistrer l'objet, ou le charger directement à partir d'un fichier enregistré existant. (Bien sûr, je dois le générer et l'enregistrer la première fois, mais après cela, j'ai la possibilité de le charger.)LOAD = TRUE
contourne l'utilisation de ma fonction compliquée et évite tous les calculs lourds qui s'y trouvent. Cette méthode nécessite toujours suffisamment de mémoire pour stocker l'objet d'intérêt, mais elle vous évite d'avoir à le calculer chaque fois que vous exécutez votre code. Pour les objets qui nécessitent beaucoup de calculs lourds d'étapes intermédiaires (par exemple, pour les calculs impliquant des boucles sur de grands tableaux), cela peut économiser beaucoup de temps et de calcul.la source
Fonctionnement
De temps en temps, R aide également à libérer la mémoire inutilisée mais toujours non libérée.
la source
for
boucle ici? Il n'y a pasi
dans l'gc
appel.gc(reset = T)
neuf foisVous pouvez également obtenir des avantages en utilisant knitr et en plaçant votre script dans des morceaux Rmd.
Je divise généralement le code en différents morceaux et sélectionne celui qui enregistrera un point de contrôle à mettre en cache ou dans un fichier RDS, et
Là-bas, vous pouvez définir un morceau à enregistrer dans le "cache", ou vous pouvez décider d'exécuter ou non un morceau particulier. De cette façon, lors d'une première exécution, vous ne pouvez traiter que la "partie 1", une autre exécution, vous ne pouvez sélectionner que la "partie 2", etc.
Exemple:
Comme effet secondaire, cela pourrait également vous éviter des maux de tête en termes de reproductibilité :)
la source
Sur la base des réponses de @ Dirk et @ Tony, j'ai fait une légère mise à jour. Le résultat sortait
[1]
avant les jolies valeurs de taille, j'ai donc retiré lecapture.output
qui a résolu le problème:la source
J'essaie de garder la petite quantité d'objets lorsque je travaille dans un projet plus grand avec beaucoup d'étapes intermédiaires. Ainsi, au lieu de créer de nombreux objets uniques appelés
dataframe
->step1
->step2
->step3
->result
raster
->multipliedRast
->meanRastF
->sqrtRast
->resultRast
Je travaille avec des objets temporaires que j'appelle
temp
.dataframe
->temp
->temp
->temp
->result
Ce qui me laisse moins de fichiers intermédiaires et plus d'aperçu.
Pour économiser plus de mémoire, je peux simplement le retirer
temp
lorsqu'il n'est plus nécessaire.Si j'ai besoin de plusieurs fichiers intermédiaires, je l' utilise
temp1
,temp2
,temp3
.Pour tester , je l' utilise
test
,test2
...la source