更糟糕的罪恶:副作用还是传递大量物体?

时间:2008-09-17 03:39:17

标签: memory r function global-variables side-effects

我在函数内部的循环中有一个函数。内部函数在内存中获取并存储大量数据(作为全局变量......我使用“R”,类似于“S-Plus”)。循环遍历要获取的长数据列表。外部函数启动该过程并传入要获取的数据集列表。

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

我编写了内部函数来存储每个数据集,然后再移动到下一个数据集,因此外部函数的所有工作都作为全局变量的副作用发生...一个大禁忌。这比收集和返回一个巨大的,占用记忆的向量矢量更好还是更糟?是否有优越的第三种方法?

如果我将数据向量存储在数据库而不是内存中,答案会改变吗?理想情况下,我希望能够终止该功能(或由于网络超时而使其失败),而不会丢失在终止之前处理的所有信息。

7 个答案:

答案 0 :(得分:9)

在外部函数中使用变量而不是全局变量。这将为您提供两种方法中的最佳方法:您不会改变全局状态,也不会复制大量数据。如果您必须提前退出,只需返回部分结果。

(参见R手册中的“范围”部分:http://cran.r-project.org/doc/manuals/R-intro.html#Scope

答案 1 :(得分:6)

记住你的Knuth。 “过早优化是所有编程邪恶的根源。”

试试副作用免费版。看它是否符合您的性能目标。如果确实如此,那么首先你没有问题;如果没有,那么使用副作用,并为下一个程序员记下你的手被迫。

答案 2 :(得分:4)

这对内存使用没有太大影响,所以你不妨让代码干净。

由于R对变量具有copy-on-modify,因此修改全局对象将具有与在返回值中传递内容相同的内存含义。

如果将输出存储在数据库中(甚至存储在文件中),则不会出现内存使用问题,并且数据将在创建时逐步可用,而不是仅在最后。数据库的速度是否更快主要取决于您使用的内存量:减少垃圾收集是为了支付写入磁盘的成本。

R中有时间和内存分析器,因此您可以凭经验查看影响。

答案 3 :(得分:1)

我不确定我是否理解这个问题,但我有几个解决方案。

  1. 在函数内部,创建一个向量列表并返回该向量。

  2. 在函数内部,创建一个环境并存储其中的所有向量。只要确保在出现错误时返回环境。

  3. 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)
    }
    

    雷夫

答案 4 :(得分:1)

仅供参考,这是一个避免副作用的完整样本玩具解决方案:

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]])

答案 5 :(得分:0)

第三种方法:内部函数返回对大数组的引用,然后循环中的下一个语句解除引用并存储在需要它的任何地方(理想情况下使用单个指针存储而不必记忆整个数组)。

这消除了副作用和大数据结构的传递。

答案 6 :(得分:-1)

如果不知道使用的语言/编译器,很难肯定地说。但是,如果您只是将指针/引用传递给您正在创建的对象,那么对象本身的大小与函数调用的速度无关。在未来操纵这些数据可能是另一回事。