R управление памятью с помощью mclapply и data.table
У меня действительно большой объект данных X (скажем, 10+ ГБ). Я хочу сделать некоторые операции в категориях внутри объекта, параллельно, чтобы сделать их быстрыми (например, много подходов модели прогнозирования). С точки зрения использования оперативной памяти, это более эффективно:
1) передать весь объект data.table дочерним процессам, затем выполнить операции над всем объектом (такие как поднаборы, но могут быть и другие вещи), затем выполнить интенсивные операции над меньшим набором данных. Поскольку мы разветвляемся, значит ли это, что дети не сделают копию X, если мы не изменим ее? например, если мы зададим подмножество X и присвоим ему значение v (как в приведенном ниже примере), использует ли дочерний процесс только дополнительную оперативную память для v или создает копию X внутри дочернего элемента, так что фактически все порожденные дочерние процессы потребляют RAM размер X в дополнение к родительскому.
2) разбить данные в родительском элементе на список, где каждый элемент содержит необходимые данные для выполнения интенсивных операций, а затем передать в дочерние разветвленные процессы только то, что требуется? Этот подход означает, что мы эффективно удваиваем объем памяти, использованный в родительском элементе, перед запуском mclapply, потому что список фактически содержит все те же данные в исходной таблице больших данных, но разделен правильно.
Вот игрушечный пример, делающий то, что обычно бывает в data.table тривиальным в любом случае без mclapply, но суть вопроса:
X <- data.table(x1 = rnorm(1:1000), category = rep(c("a", "b", "c", "d"), 250))
# 1 Pass in the big object:
doIntensiveStuff <- function(y, cat) {
# y is big, all categories
# now going to operate on all of y, to subset the rows we need, then do calculations we need
v <- y[category == cat,]
mean(v[, x1])
}
z <- lapply(X = X[, unique(category)], FUN = doIntensiveStuff, y = X)#, mc.cores = length(X[, unique(category)]))
# First approach, pass in the X <- data.table(x1 = rnorm(1:1000), category = rep(c("a", "b", "c", "d"), 250))
doIntensiveStuff <- function(y, cat) {
# y is big, all categories
# now going to operate on all of y, to subset the rows we need, then do calculations we need
v <- y[category == cat,]
mean(v[, x1])
}
z <- lapply(X = X[, unique(category)], FUN = doIntensiveStuff, y = X)#, mc.cores = length(X[, unique(category)]))
# Second approach. Double the data in the parent, but the forked children get only what they need:
doIntensiveStuff2 <- function(yJustOneCategory, cat) {
# yJustOneCategory is a smaller object inside the child forked process, while the large X object remains in the parent.
yJustOneCategory[, mean(x1)]
}
z2 <- mclapply(X = split(X, by = "category"), FUN = doIntensiveStuff2, mc.cores = length(X[, unique(category)]))
all.equal(as.numeric(unlist(z)), as.numeric(unlist(z2)))
# TRUE
doIntensiveStuff2 <- function(yJustOneCategory, cat) {
# yJustOneCategory is a smaller object inside the child forked process, while the large X object remains in the parent.
yJustOneCategory[, mean(x1)]
}
z2 <- mclapply(X = split(X, by = "category"), FUN = doIntensiveStuff2, mc.cores = length(X[, unique(category)]))
all.equal(as.numeric(unlist(z)), as.numeric(unlist(z2)))
# TRUE
Буду признателен за разъяснение моего понимания того, как память используется в R для разветвленных процессов с большими данными.