Подтвердить что ты не робот

При добавлении списка в R в результате копирования?

Предположим, что я создал список из R и добавлю его следующим образом:

x = list(10)
x[[2]] = 20

Это эквивалентно

x = list(10)
x = list(10, 20)

? Я не очень разбираюсь в конкретных деталях того, как R обрабатывает списки в памяти, но мое ограниченное понимание состоит в том, что он имеет тенденцию быть счастливым от копирования; то, что было бы идеальным для меня, было бы то, что первый вариант не предполагает по существу создания другого списка в памяти, а просто приводит к тому, чтобы отложить новое место в памяти для добавленного значения. По сути, если у меня есть большой список, я не хочу, чтобы R сделал еще одну копию, если я просто хочу что-то добавить к нему.

Если поведение, которое я хочу, не то, что здесь дано, есть ли другой способ получить желаемый эффект?

4b9b3361

Ответ 1

Я уверен, что ответ "нет". Я использовал следующий код для двойной проверки:

Rprof(tmp <- tempfile(), memory.profiling = TRUE)

x <- list()
for (i in 1:100) x[[i]] <- runif(10000)

Rprof()
summaryRprof(tmp, memory = "stats")
unlink(tmp)

Выход:

# index: runif
#      vsize.small  max.vsize.small      vsize.large  max.vsize.large 
#            76411           381781           424523          1504387 
#            nodes        max.nodes     duplications tot.duplications 
#          2725878         13583136                0                0 
#          samples 
#                5 

Соответствующая часть duplications = 0.

Ответ 2

Matthew Dowle answer здесь, и обоснование эффективности работы с памятью состоит в том, чтобы остановить множество копий за кулисами <-, [<-, [[<- и другие базовые операции R (names и т.д.)

[[<- скопирует все x. См. Пример ниже

x <- list(20)
 tracemem(x)
#[1] "<0x2b0e2790>"
 x[[2]] <- 20
# tracemem[0x2b0e2790 -> 0x2adb7798]: 

Ваш второй случай

x <- list(10,20)

на самом деле не добавляет оригинальный x, а заменяет x на объект, который является оригинальным x с добавленным значением.

Ответ 3

Чтобы помочь мне разобраться, внесите ли изменение список из глубокой копии или мелкой копии, я создал небольшой эксперимент. Если изменение списка делает глубокую копию, то при изменении списка, содержащего большой объект, по сравнению со списком, содержащим небольшой объект, он должен быть медленнее:

z1 <- list(runif(1e7))
z2 <- list(1:10)

system.time({
  for(i in 1:1e4) z1[1 + i] <- 1L
})
#  user  system elapsed
# 0.283   0.034   0.317
system.time({
  for(i in 1:1e4) z2[1 + i] <- 1L
})
#  user  system elapsed
# 0.284   0.034   0.319

Тайминги на моем компьютере были в основном идентичными, предполагая, что копирование списка делает мелкую копию, копируя указатели на существующие структуры данных.

Ответ 4

Принял ответ на фланель, но подсказка Chase была хорошей, поэтому я подтвердил, что у меня есть желаемое поведение, используя его предложение использовать tracemem(). Вот первый пример, который мы просто добавляем в список:

x = list(10)
tracemem(x[[1]])
# [1] "<0x2d03fa8>" #(likely different on each machine)
x[[2]] = 20
tracemem(x[[1]])
# [1] "<0x2d03fa8>"

И вот результат из второго примера, где мы создаем два списка:

x = list(10)
tracemem(x[[1]])
# [1] "<0x2d03c78>"
x = list(10, 20)
tracemem(x[[1]])
# [1] "<0x2d07ff8>"

Таким образом, первый метод дает желаемое поведение.