Избегайте копирования больших объектов в R — делает ли функция get () копию?

У меня есть унаследованный код R, который использует идиому, чтобы избежать копирования больших кадров данных. Когда функция, создающая фрейм данных, завершает работу, она не возвращает фрейм данных, а вместо этого сохраняет его в глобальном контексте среды:

assign(df_name, df, envir = globalenv())

После завершения функции вызывающий код извлекает фрейм данных следующим образом:

df <- get(df_name, envir = globalenv())

Мой вопрос заключается в следующем: действительно ли сама функция get() создает копию, когда возвращает значение, тем самым создавая ту самую копию, которой эта идиома должна была избежать? Если да, то есть ли лучший способ сделать это?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
242
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Одним словом, это ерунда. Здесь копируются назначения два (как через assign, так и через <-). Просто возврат объекта из функции сохранил бы одну из копий.

Между прочим, само копирование — это дешевый, поскольку R реализует концепцию, называемую семантикой «копирование при записи»: приведенные выше команды логически делают копии, но физически это только увеличивает счетчик ссылок внутри скопированной ссылки. Фактические данные по ссылке копируются только тогда, когда вы изменяете данные через одну из ссылок.

Насколько я знаю, возврат чего-либо на самом деле не копирует это из одной области памяти в другую. Пара тестов:

trace_return <- function() {
  df <- data.frame(a = 1:10, b = letters[1:10])
  print(tracemem(df))
  df
}

ans <- trace_return()
# "<00000188C114D578>"
ans$a[1L] <- 0L # copy triggered on modify
# tracemem[0x00000188c114d578 -> 0x00000188c10ab098]: 
# tracemem[0x00000188c10ab098 -> 0x00000188c10ab258]: $<-.data.frame $<- 
# tracemem[0x00000188c10ab258 -> 0x00000188c10ab358]: $<-.data.frame $<-

А также:

e <- new.env()
e$ans <- trace_return()
# "<00000188C13F8EF8>"
ans <- e$ans # no copy here
ans$b <- NULL
# tracemem[0x00000188c13f8ef8 -> 0x00000188c14293f8]: 
# tracemem[0x00000188c14293f8 -> 0x00000188c1429378]: $<-.data.frame $<- 
# tracemem[0x00000188c1429378 -> 0x00000188c1429278]: $<-.data.frame $<- 

На самом деле не имеет значения, есть ли две копии одного и того же data.frame. Но если честно я не вижу смысла присваивать data.frame дважды.

Вы можете использовать пакет pryr, чтобы увидеть, сколько памяти выделяет объект.

library(pryr)

df <- do.call(data.frame, replicate(8000, rep(FALSE, 8000), simplify=FALSE))

assign("dname_out", df, envir = globalenv())

mem_used()
337 MB

mem_change(df <- get("dname_out", envir = globalenv()))

736 B

> mem_used()
337 MB

Изменение в памяти составляет всего 736 байт, поэтому на самом деле вы не можете разбить свой компьютер, создавая тонны копий.

Другие вопросы по теме