Многоядерность и использование памяти в R под Ubuntu - PullRequest
27 голосов
/ 24 февраля 2011

Я использую R на рабочей станции Ubuntu с 8 виртуальными ядрами и 8 ГБ оперативной памяти.Я надеялся регулярно использовать многоядерный пакет, чтобы использовать 8 ядер параллельно;однако я считаю, что весь процесс R дублируется 8 раз.Поскольку кажется, что R фактически использует гораздо больше памяти, чем сообщается в gc (в 5 раз, даже после gc ()), это означает, что даже относительно умеренное использование памяти (один объект 200 МБ) становится непозволительно тяжелым, после дублирования 8 раз,Я изучил большую память, чтобы дочерние процессы разделяли одно и то же пространство памяти;но это потребовало бы некоторого серьезного переписывания моего кода, так как он не имеет отношения к фреймам данных.

Есть ли способ сделать R как можно более обедненным, прежде чем разветвляться, то есть освободить ли ОС столько памяти, сколько возможно?

РЕДАКТИРОВАТЬ: Я думаю, что я понимаю, что происходит сейчас.Проблема не в том, где я думал - объекты, которые существуют в родительском потоке и не обрабатываются, не дублируются восемь раз.Вместо этого моя проблема, я полагаю, возникла из-за характера манипуляций, которые я заставляю выполнять каждый дочерний процесс.Каждый из них должен манипулировать большим фактором с сотнями тысяч уровней, и я думаю, , это - это большой объем памяти.В результате это действительно тот случай, когда общая загрузка памяти пропорциональна количеству ядер;но не так драматично, как я думал.Другой урок, который я выучил, состоит в том, что с 4 физическими ядрами + возможностью гиперпоточности, гиперпоточность обычно не является хорошей идеей для R. Усиление минимально, а стоимость памяти может быть нетривиальной.Так что теперь я буду работать над четырьмя ядрами.

Для тех, кто хотел бы поэкспериментировать, это тип кода, который я запускал:

# Create data
sampdata <- data.frame(id = 1:1000000)
for (letter in letters) {
sampdata[, letter] <- rnorm(1000000)
}
sampdata$groupid = ceiling(sampdata$id/2)

# Enable multicore
library(multicore)
options(cores=4) # number of cores to distribute the job to

# Actual job
system.time(do.call("cbind", 
    mclapply(subset(sampdata, select = c(a:z)), function(x) tapply(x, sampdata$groupid, sum))
))

Ответы [ 2 ]

17 голосов
/ 12 марта 2011

Вы пробовали data.table ?

> system.time(ans1 <- do.call("cbind",
lapply(subset(sampdata,select=c(a:z)),function(x)tapply(x,sampdata$groupid,sum))
))
   user  system elapsed 
906.157  13.965 928.645 

> require(data.table)
> DT = as.data.table(sampdata)
> setkey(DT,groupid)
> system.time(ans2 <- DT[,lapply(.SD,sum),by=groupid])
   user  system elapsed 
186.920   1.056 191.582                # 4.8 times faster

> # massage minor diffs in results...
> ans2$groupid=NULL
> ans2=as.matrix(ans2)
> colnames(ans2)=letters
> rownames(ans1)=NULL

> identical(ans1,ans2)
[1] TRUE

Ваш пример очень интересный.Он достаточно большой (200 МБ), есть много групп (1/2 миллиона), и каждая группа очень мала (2 строки).191-е годы, вероятно, могут быть значительно улучшены, но, по крайней мере, это начало.[Март 2011]


И теперь эта идиома (то есть lapply(.SD,...)) значительно улучшилась.С версией 1.8.2 и на более быстром компьютере, чем тест, описанный выше, и с последней версией R и т. Д., Вот обновленное сравнение:

sampdata <- data.frame(id = 1:1000000)
for (letter in letters) sampdata[, letter] <- rnorm(1000000)
sampdata$groupid = ceiling(sampdata$id/2)
dim(sampdata)
# [1] 1000000      28
system.time(ans1 <- do.call("cbind",
  lapply(subset(sampdata,select=c(a:z)),function(x)
    tapply(x,sampdata$groupid,sum))
))
#   user  system elapsed
# 224.57    3.62  228.54
DT = as.data.table(sampdata)
setkey(DT,groupid)
system.time(ans2 <- DT[,lapply(.SD,sum),by=groupid])
#   user  system elapsed
#  11.23    0.01   11.24                # 20 times faster

# massage minor diffs in results...
ans2[,groupid:=NULL]
ans2[,id:=NULL]
ans2=as.matrix(ans2)
rownames(ans1)=NULL

identical(ans1,ans2)
# [1] TRUE


sessionInfo()
R version 2.15.1 (2012-06-22)
Platform: x86_64-pc-mingw32/x64 (64-bit)

locale:
[1] LC_COLLATE=English_United Kingdom.1252   LC_CTYPE=English_United Kingdom.1252
[3] LC_MONETARY=English_United Kingdom.1252  LC_NUMERIC=C
[5] LC_TIME=English_United Kingdom.1252

attached base packages:
[1] stats     graphics  grDevices datasets  utils     methods   base     

other attached packages:
[1] data.table_1.8.2 RODBC_1.3-6     
2 голосов
/ 03 августа 2012

Вещи, которые я пробовал на Ubuntu 64 bit R, ранжированы по порядку успеха:

  • Работайте с меньшим количеством ядер, как и вы.

  • Разделите задания mclapply на части и сохраните частичные результаты в базе данных, используя DBI с append = TRUE.

  • Используйте функцию rm вместе с gc() часто

Я попробовал все это, и mclapply все еще начинает создавать все более и более крупные процессы во время работы, что заставляет меня подозревать, что каждый процесс удерживает какую-то остаточную память, которая ему действительно не нужна.

P.S. Я использовал data.table, и кажется, что каждый дочерний процесс копирует data.table.

...