Хуже грех: побочные эффекты или прохождение массивных объектов? - PullRequest
5 голосов
/ 17 сентября 2008

У меня есть функция внутри цикла внутри функции. Внутренняя функция получает и хранит большой вектор данных в памяти (как глобальная переменная ... Я использую "R", который похож на "S-Plus"). Цикл проходит по длинному списку данных, которые будут получены. Внешняя функция запускает процесс и передает список наборов данных, которые нужно получить.

for (dataset in list_of_datasets) {
  for (datachunk in dataset) {
    <process datachunk>
    <store result? as vector? where?>
  }
}

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

Изменился бы ответ, если бы я сохранял векторы данных в базе данных, а не в памяти? В идеале я хотел бы иметь возможность завершить работу функции (или вызвать ее сбой из-за тайм-аутов сети) без потери всей информации, обработанной до завершения.

Ответы [ 7 ]

9 голосов
/ 17 сентября 2008

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

(см. Раздел «Область действия» в руководстве по R: http://cran.r -project.org / doc / manual / R-intro.html # Scope )

6 голосов
/ 17 сентября 2008

Помни свой Кнут. «Преждевременная оптимизация - корень всего зла программирования».

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

4 голосов
/ 17 сентября 2008

Это не будет иметь большого значения для использования памяти, так что вы могли бы также сделать код чистым.

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

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

В R есть как профилировщики времени, так и профили памяти, поэтому вы можете эмпирически увидеть, как это влияет.

1 голос
/ 17 сентября 2008

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

outerfunc <- function(names) {
  templist <- list()
  for (aname in names) {
    templist[[aname]] <- innerfunc(aname)
  }
  templist
}

innerfunc <- function(aname) {
  retval <- NULL
  if ("one" %in% aname) retval <- c(1)
  if ("two" %in% aname) retval <- c(1,2)
  if ("three" %in% aname) retval <- c(1,2,3)
  retval
}

names <- c("one","two","three")

name_vals <- outerfunc(names)

for (name in names) assign(name, name_vals[[name]])
1 голос
/ 17 сентября 2008

Я не уверен, что понимаю вопрос, но у меня есть пара решений.

  1. Внутри функции создайте список векторов и верните его.

  2. Внутри функции создайте среду и сохраните в ней все векторы. Просто убедитесь, что вы возвращаете среду в случае ошибок.

в R:

help(environment)

# You might do something like this:

outer <- function(datasets) {
  # create the return environment
  ret.env <- new.env()
  for(set in dataset) {
    tmp <- inner(set)
    # check for errors however you like here.  You might have inner return a list, and
    # have the list contain an error component
    assign(set, tmp, envir=ret.env)
  }
  return(ret.env)
}

#The inner function might be defined like this

inner <- function(dataset) {
  # I don't know what you are doing here, but lets pretend you are reading a data file
  # that is named by dataset
  filedata <- read.table(dataset, header=T)
  return(filedata)
}

Leif

0 голосов
/ 17 сентября 2008

Третий подход: внутренняя функция возвращает ссылку на большой массив, который следующий оператор внутри цикла затем разыменовывает и сохраняет там, где это необходимо (в идеале с одним хранилищем указателей, а не с помощью необходимости скопировать весь массив). *

Это избавляет как от побочного эффекта, так и от прохождения больших структур данных.

0 голосов
/ 17 сентября 2008

Трудно сказать однозначно, не зная используемый язык / компилятор. Однако, если вы можете просто передать указатель / ссылку на объект, который вы создаете, тогда размер самого объекта не имеет ничего общего со скоростью вызовов функций. Управление этими данными в будущем может быть другой историей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...