Quais truques as pessoas usam para gerenciar a memória disponível de uma sessão R interativa? Uso as funções abaixo [baseadas nas postagens de Petr Pikal e David Hinds na lista de ajuda em 2004] para listar (e / ou classificar) os maiores objetos e ocasionalmenterm()
alguns deles. Mas, de longe, a solução mais eficaz era ... rodar no Linux de 64 bits com ampla memória.
Quaisquer outros truques legais que as pessoas querem compartilhar? Um por post, por favor.
# 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
fonte
fonte
multilevelPSA
pacote . O pacote foi projetado para outra coisa, mas você pode usar a função de lá sem carregar o pacote dizendorequireNamespace(multilevelPSA); multilevelPSA::lsos(...)
. Ou noDmisc
pacote (não no CRAN).Respostas:
Certifique-se de registrar seu trabalho em um script reproduzível. De tempos em tempos, reabra R e, em seguida,
source()
seu script. Você limpará tudo o que não estiver mais usando e, como benefício adicional, testará seu código.fonte
1-load.r
,2-explore.r
,3-model.r
- de que maneira é óbvio para os outros que há alguma ordem presente.Eu uso o pacote data.table . Com seu
:=
operador, você pode:Nenhuma dessas operações copia o (potencialmente grande)
data.table
, nem mesmo uma vez.data.table
usa muito menos memória de trabalho.Links Relacionados :
:=
operador em data.table?fonte
Vi isso em um post no twitter e acho que é uma função incrível de Dirk! Seguindo a resposta de JD Long, eu faria isso para uma leitura amigável:
O que resulta em algo como o seguinte:
NOTA: A parte principal que adicionei foi (novamente, adaptada da resposta de JD):
fonte
capture.output
não é mais necessário eobj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })
produz uma saída limpa. De fato, não removê-lo produz aspas indesejadas na saída, ou seja, em[1] "792.5 Mb"
vez de792.5 Mb
.obj.class <- napply(names, function(x) as.character(class(x))[1])
para,obj.class <- napply(names, function(x) class(x)[1])
desdeclass
sempre, retornar um vetor de caracteres agora (base-3.5.0).Uso agressivamente o
subset
parâmetro com seleção apenas das variáveis necessárias ao passar quadros de dados para odata=
argumento das funções de regressão. Isso resulta em alguns erros se eu esquecer de adicionar variáveis à fórmula e aoselect=
vetor, mas ainda poupa muito tempo devido à diminuição da cópia de objetos e reduz significativamente a pegada de memória. Digamos que tenho 4 milhões de registros com 110 variáveis (e tenho). Exemplo:Por meio da definição do contexto e da estratégia: a
gdlab2
variável é um vetor lógico que foi construído para indivíduos em um conjunto de dados que tinha todos os valores normais ou quase normais para vários testes de laboratório eHIVfinal
era um vetor de caracteres que resumia os testes preliminares e confirmatórios para o HIV .fonte
Eu amo o script .ls.objects () de Dirk, mas continuei apertando os olhos para contar caracteres na coluna de tamanho. Então eu fiz alguns hacks feios para torná-lo presente com formatação bonita para o tamanho:
fonte
Esse é um bom truque.
Uma outra sugestão é usar objetos com eficiência de memória sempre que possível: por exemplo, use uma matriz em vez de um data.frame.
Isso realmente não trata do gerenciamento de memória, mas uma função importante que não é amplamente conhecida é memory.limit (). Você pode aumentar o padrão usando este comando, memory.limit (tamanho = 2500), em que o tamanho está em MB. Como Dirk mencionou, você precisa usar 64 bits para tirar proveito real disso.
fonte
Eu gosto bastante da função de objetos aprimorados desenvolvida por Dirk. Na maioria das vezes, porém, uma saída mais básica com o nome e o tamanho do objeto é suficiente para mim. Aqui está uma função mais simples com um objetivo semelhante. O uso da memória pode ser ordenado alfabeticamente ou por tamanho, pode ser limitado a um certo número de objetos e pode ser ordenado ascendente ou descendente. Além disso, costumo trabalhar com dados com mais de 1 GB, portanto a função altera as unidades de acordo.
E aqui está um exemplo de saída:
fonte
Eu nunca salvei um espaço de trabalho R. Uso scripts de importação e scripts de dados e produzo objetos de dados especialmente grandes que não quero recriar frequentemente para arquivos. Dessa forma, sempre começo com um novo espaço de trabalho e não preciso limpar objetos grandes. Essa é uma função muito boa embora.
fonte
Infelizmente, não tive tempo de testá-lo extensivamente, mas aqui está uma dica de memória que eu não tinha visto antes. Para mim, a memória necessária foi reduzida em mais de 50%. Quando você lê coisas no R com, por exemplo, read.csv, elas exigem uma certa quantidade de memória. Depois disso, você pode salvá-los com.
save("Destinationfile",list=ls())
Na próxima vez que você abrir R, poderá usarload("Destinationfile")
Agora o uso da memória pode ter diminuído. Seria bom se alguém pudesse confirmar se isso produz resultados semelhantes com um conjunto de dados diferente.fonte
fread
e salvos em .RData. Os arquivos RData eram de fato cerca de 70% menores, mas após o recarregamento, a memória usada era exatamente a mesma. Esperava que esse truque reduzisse a pegada de memória ... estou perdendo alguma coisa?Para ilustrar ainda mais a estratégia comum de reinicializações frequentes, podemos usar o littler, que permite executar expressões simples diretamente da linha de comando. Aqui está um exemplo que às vezes uso para cronometrar BLAS diferentes para um simples produto cruzado.
Da mesma forma,
carrega o pacote Matrix (via opção --packages | -l) e executa os exemplos da função spMatrix. Como o r sempre inicia 'novo', esse método também é um bom teste durante o desenvolvimento do pacote.
Por último, mas não menos importante, r também funciona muito bem no modo de lote automatizado em scripts usando o cabeçalho shebang '#! / Usr / bin / r'. O Rscript é uma alternativa onde o littler não está disponível (por exemplo, no Windows).
fonte
Para propósitos de velocidade e memória, ao criar um quadro de dados grande por meio de uma série complexa de etapas, eu o lavarei periodicamente (o conjunto de dados em andamento sendo construído) no disco, anexando tudo o que veio antes e depois o reiniciando . Dessa forma, as etapas intermediárias estão funcionando apenas em quadros de dados pequenos (o que é bom porque, por exemplo, rbind diminui consideravelmente com objetos maiores). O conjunto de dados inteiro pode ser lido novamente no final do processo, quando todos os objetos intermediários tiverem sido removidos.
fonte
Apenas para observar que os
data.table
pacotestables()
parecem ser um bom substituto para a.ls.objects()
função personalizada do Dirk (detalhada nas respostas anteriores), embora apenas para data.frames / tables e não por exemplo matrizes, matrizes, listas.fonte
Tenho sorte e meus grandes conjuntos de dados são salvos pelo instrumento em "pedaços" (subconjuntos) de aproximadamente 100 MB (binário de 32 bits). Assim, eu posso executar as etapas de pré-processamento (excluir peças não informativas, reduzir a amostragem) sequencialmente antes de fundir o conjunto de dados.
Chamar
gc ()
"manualmente" pode ajudar se o tamanho dos dados se aproximar da memória disponível.Às vezes, um algoritmo diferente precisa de muito menos memória.
Às vezes, há uma troca entre vetorização e uso de memória.
compare:
split
&lapply
vs. umfor
loop.Para uma análise rápida e fácil dos dados, geralmente trabalho primeiro com um pequeno subconjunto aleatório (
sample ()
) dos dados. Depois que o script de análise de dados / .Rnw terminar o código de análise de dados e os dados completos forem para o servidor de cálculo para cálculo durante a noite / no final de semana / ....fonte
O uso de ambientes em vez de listas para manipular coleções de objetos que ocupam uma quantidade significativa de memória de trabalho.
O motivo: sempre que um elemento de uma
list
estrutura é modificado, a lista inteira é temporariamente duplicada. Isso se torna um problema se o requisito de armazenamento da lista for cerca da metade da memória de trabalho disponível, pois os dados deverão ser trocados para o disco rígido lento. Os ambientes, por outro lado, não estão sujeitos a esse comportamento e podem ser tratados de maneira semelhante às listas.Aqui está um exemplo:
Em conjunto com estruturas como
big.matrix
oudata.table
que permitem alterar o conteúdo no local, é possível obter um uso muito eficiente da memória.fonte
A
ll
função nogData
pacote também pode mostrar o uso da memória de cada objeto.fonte
Se você realmente deseja evitar vazamentos, evite criar grandes objetos no ambiente global.
O que costumo fazer é ter uma função que faça o trabalho e retorne
NULL
- todos os dados são lidos e manipulados nessa função ou em outras que ela chama.fonte
Com apenas 4 GB de RAM (executando o Windows 10, então faça isso com 2 ou mais 1 GB de forma realista), tive que ter muito cuidado com a alocação.
Eu uso data.table quase exclusivamente.
A função 'fread' permite agrupar informações por nomes de campos na importação; importe apenas os campos realmente necessários para começar. Se você estiver usando a leitura base R, anule as colunas falsas imediatamente após a importação.
Como 42- sugere, sempre que possível, subconfigurarei as colunas imediatamente após importar as informações.
Eu frequentemente rm () objetos do ambiente assim que eles não são mais necessários, por exemplo, na próxima linha depois de usá-los para definir um subconjunto de outra coisa, e chamo gc ().
'fread' e 'fwrite' de data.table podem ser muito rápidos em comparação com as leituras e gravações básicas R.
Como o kpierce8 sugere, quase sempre escrevo tudo fora do ambiente e o luto de volta, mesmo com milhares / centenas de milhares de pequenos arquivos para passar. Isso não apenas mantém o ambiente 'limpo' e mantém a alocação de memória baixa, mas, possivelmente devido à grave falta de RAM disponível, o R tem uma propensão a travar frequentemente no meu computador; com muita frequência. Ter o backup das informações na própria unidade à medida que o código progride em vários estágios significa que não preciso iniciar desde o início, se houver uma falha.
A partir de 2017, acho que os SSDs mais rápidos estão rodando em torno de alguns GB por segundo através da porta M2. Eu tenho um SSD Kingston V300 (550MB / s) realmente básico de 50 GB que eu uso como disco principal (possui Windows e R). Eu mantenho todas as informações em massa em um prato WD barato de 500 GB. Movo os conjuntos de dados para o SSD quando começo a trabalhar neles. Isso, combinado com 'fread'ing e' fwrite'ing, tudo tem funcionado muito bem. Eu tentei usar 'ff', mas prefiro o primeiro. As velocidades de leitura / gravação em 4K podem criar problemas com isso; fazer o backup de um quarto de milhão de arquivos de 1k (no valor de 250 MB) do SSD para o prato pode levar horas. Tanto quanto sei, ainda não existe nenhum pacote R que possa otimizar automaticamente o processo de 'chunkification'; por exemplo, observe quanta RAM um usuário possui, teste as velocidades de leitura / gravação da RAM / todas as unidades conectadas e, em seguida, sugira um protocolo ideal de 'chunkification'. Isso poderia produzir algumas melhorias significativas no fluxo de trabalho / otimizações de recursos; por exemplo, divida-o em ... MB para a ram -> divida-o em ... MB no SSD -> divida-o em ... MB no prato -> divida-o em ... MB na fita. Ele poderia coletar amostras de conjuntos de dados de antemão para fornecer um indicador mais realista para trabalhar.
Muitos dos problemas nos quais trabalhei em R envolvem a formação de pares de combinação e permutação, triplos etc., o que torna a limitação de RAM limitada mais uma limitação, pois elas geralmente se expandem pelo menos exponencialmente em algum momento. Isso me fez focar muita atenção na qualidade, em vez da quantidade de informações inseridas neles, em vez de tentar limpá-las posteriormente, e na sequência de operações na preparação das informações (começando com a operação mais simples e aumentando a complexidade); por exemplo, subconjunto, em seguida, mesclar / unir, formar combinações / permutações etc.
Parece haver alguns benefícios em usar a base R de leitura e gravação em alguns casos. Por exemplo, a detecção de erros no 'fread' é tão boa que pode ser difícil tentar obter informações realmente confusas no R para começar a limpá-las. A base R também parece ser muito mais fácil se você estiver usando Linux. A base R parece funcionar bem no Linux, o Windows 10 usa ~ 20 GB de espaço em disco, enquanto o Ubuntu precisa apenas de alguns GB, a RAM necessária com o Ubuntu é um pouco menor. Mas notei grandes quantidades de avisos e erros ao instalar pacotes de terceiros no (L) Ubuntu. Eu não recomendaria me afastar muito do (L) Ubuntu ou de outras distribuições de estoque com o Linux, pois você pode perder tanta compatibilidade geral que torna o processo quase inútil (acho que a 'unidade' deve ser cancelada no Ubuntu a partir de 2017 )
Espero que parte disso possa ajudar outras pessoas.
fonte
Isso não acrescenta nada ao exposto, mas está escrito no estilo simples e muito comentado que eu gosto. Ele gera uma tabela com os objetos ordenados em tamanho, mas sem alguns dos detalhes fornecidos nos exemplos acima:
fonte
Esta é uma resposta mais recente para esta excelente e antiga pergunta. Do Hadley's Advanced R:
( http://adv-r.had.co.nz/memory.html )
fonte
Se você está trabalhando no Linux e deseja usar vários processos e precisa executar operações de leitura em um ou mais objetos grandes, use em
makeForkCluster
vez de amakePSOCKcluster
. Isso também poupa o tempo de envio do objeto grande para os outros processos.fonte
Eu realmente aprecio algumas das respostas acima, seguindo @hadley e @Dirk que sugerem fechar R e emitir
source
e usar a linha de comando, eu venho com uma solução que funcionou muito bem para mim. Eu tive que lidar com centenas de espectros de massa, cada um ocupando cerca de 20 Mb de memória, então usei dois scripts R, da seguinte maneira:Primeiro um invólucro:
com esse script, basicamente controlo o que meu script principal faz
runConsensus.r
e escrevo a resposta dos dados para a saída. Com isso, cada vez que o wrapper chama o script, parece que o R é reaberto e a memória é liberada.Espero que ajude.
fonte
Além das técnicas mais gerais de gerenciamento de memória fornecidas nas respostas acima, sempre tento reduzir o tamanho dos meus objetos o máximo possível. Por exemplo, trabalho com matrizes muito grandes, mas muito esparsas, ou seja, matrizes em que a maioria dos valores é zero. Usando o pacote 'Matrix' (capitalização importante), pude reduzir meus tamanhos médios de objetos de ~ 2 GB para ~ 200 MB, simplesmente como:
O pacote Matrix inclui formatos de dados que podem ser usados exatamente como uma matriz comum (sem necessidade de alterar seu outro código), mas são capazes de armazenar dados esparsos com muito mais eficiência, sejam carregados na memória ou salvos no disco.
Além disso, os arquivos brutos que eu recebo estão no formato 'longo', onde cada ponto de dados tem variáveis
x, y, z, i
. Muito mais eficiente para transformar os dados em umax * y * z
matriz de dimensão com apenas variáveli
.Conheça seus dados e use um pouco de bom senso.
fonte
Dica para lidar com objetos que requerem cálculo intermediário pesado: ao usar objetos que exigem muito cálculo pesado e etapas intermediárias para criar, geralmente acho útil escrever um pedaço de código com a função para criar o objeto e, em seguida, um pedaço separado de código que me dá a opção de gerar e salvar o objeto como um
rmd
arquivo ou carregá-lo externamente de umrmd
arquivo que eu já salvei anteriormente. Isso é especialmente fácil deR Markdown
usar , usando a seguinte estrutura de blocos de código.Com essa estrutura de código, tudo o que preciso fazer é mudar,
LOAD
dependendo se desejo gerar e salvar o objeto ou carregá-lo diretamente de um arquivo salvo existente. (Obviamente, tenho que gerá-lo e salvá-lo na primeira vez, mas depois disso tenho a opção de carregá-lo.) A configuraçãoLOAD = TRUE
ignora o uso da minha função complicada e evita toda a computação pesada nela. Esse método ainda requer memória suficiente para armazenar o objeto de interesse, mas evita que você precise calculá-lo sempre que executar seu código. Para objetos que requerem muito cálculo pesado de etapas intermediárias (por exemplo, para cálculos envolvendo loops em grandes matrizes), isso pode economizar uma quantidade substancial de tempo e computação.fonte
Corrida
de tempos em tempos também ajuda o R a liberar memória não utilizada, mas ainda não liberada.
fonte
for
loop faz aqui? Não hái
nagc
ligação.gc(reset = T)
nove vezesVocê também pode obter algum benefício usando o knitr e colocando seu script em chmks Rmd.
Normalmente, divido o código em diferentes partes e seleciono qual salvará um ponto de verificação para armazenar em cache ou em um arquivo RDS, e
Lá, você pode definir um pedaço para ser salvo no "cache" ou pode optar por executar ou não um pedaço específico. Dessa forma, em uma primeira execução, você pode processar apenas a "parte 1", outra execução pode selecionar apenas a "parte 2", etc.
Exemplo:
Como efeito colateral, isso também pode poupar algumas dores de cabeça em termos de reprodutibilidade :)
fonte
Com base nas respostas de @ Dirk e @ Tony, fiz uma pequena atualização. O resultado foi gerado
[1]
antes dos valores de tamanho bonito, então eu retirei ocapture.output
que resolveu o problema:fonte
Eu tento manter a quantidade de objetos pequena ao trabalhar em um projeto maior com várias etapas intermediárias. Então, em vez de criar muitos objetos exclusivos chamados
dataframe
->step1
->step2
->step3
->result
raster
->multipliedRast
->meanRastF
->sqrtRast
->resultRast
Eu trabalho com objetos temporários que eu chamo
temp
.dataframe
->temp
->temp
->temp
->result
O que me deixa com menos arquivos intermediários e mais visão geral.
Para economizar mais memória, posso simplesmente remover
temp
quando não for mais necessário.Se eu precisar de vários arquivos intermediários, eu uso
temp1
,temp2
,temp3
.Para testar eu uso
test
,test2
...fonte