3

我需要在 R 中执行涉及大量一般线性回归的离散事件模拟,因此需要将大量 R 对象一一附加到列表或数组中。现在一切都已经实现并且模拟器运行了。但是,我意识到整个过程的性能瓶颈实际上是在列表追加(模拟6000个事件大约需要15分钟)。我注意到当 R 列表、向量或数组中的对象数量变大时,插入变得非常慢。我需要一个可重用的列表数据结构,因为它在模拟代码的许多部分中一次又一次地使用。这是我用 R 编写的简单 ArrayList 的实现:

ArrayListX = setRefClass(
  Class = "ArrayListX",
  fields = list(
    ol = "array",
    len = "integer",
    cap = "integer",
    expandNum = "integer"),
  methods = list(
    initialize = function(){
      len <<- 0L
      expandNum <<- 100000L
      #TODO: The preallocation here made the performance worse.
      ol <<- array(dim = expandNum)
      cap <<- expandNum
    },
    expand = function(){
      ol <<- c(ol, array(list(), dim = expandNum))
      cap <<- cap + expandNum
    },
    get = function(i){
      return(ol[i])
    },
    remove = function(i){
      ol[i] <<- NULL
      len <<- len - 1L
    },
    add = function(obj){
      if(cap <= len)
      {
        expand()
      }
      len <<- len + 1L
      ol[len] <<- obj
      return(len)
    },
    size = function(){
      return(len)
    },
    set = function(i, obj){
      ol[i] <<- obj
    },
    insert = function(before, obj){
      if(before < 1 || before > len + 1)
        throw(paste("Subscript out of bound. before = ", before))

      if(len == 0)
      {
        ol[1] <<- obj
      }  else
      {
        head = ol[1:before - 1]
        head[before] = obj
      {
        if(before <= len)
          ol <<- c(head, ol[before:len])
        else
          ol <<- head
      }
      }
      len <<- len + 1L
    }
  )
)

请注意,我尝试初始化列表并预分配一定数量的行,这也没有提高性能,实际上它甚至比没有预分配还要慢。(这可能是因为我使用了引用类。根据我的经验,在不使用引用类时,预分配通常会提高 R 中的性能)这就是为什么我考虑使用 Java ArrayList 作为列表的后端数据结构的原因。我尝试了 rJava。

ArrayListJ = setRefClass(
  Class = "ArrayListJ",
  contains = "ArrayList",
  fields = list(
    jal = "ANY"),
  methods = list(
    initialize = function(){
      jal <<- .jnew("java.util.ArrayList")
    },
    get = function(i){
      rObj = .jcall(jal, returnSig="Ljava/lang/Object;", method = "get", as.integer(i))
      return(rObj)
    },
    remove = function(i){
    },
    add = function(obj){
      jRef = toJava(x = obj, engine = NULL)
      .jcall(obj = jal, returnSig = "Z", method = "add", .jcast(jRef, new.class = "java/lang/Object"))
    },
    size = function(){
      s = .jcall(obj = jal, returnSig = "I", method = "size")
      return (s)
    },
set = function(i, obj){
    #Not implemented yet.
    },
insert = function(before, obj){
    #Not implemented yet.
    }    
    )
  )

我现在正在努力使用“get”功能,因为我无法让它工作。它总是返回一个我不能在 R 中使用的 REXPReference 对象。我希望它返回的实际上是添加到 ArrayListJ 的原始 R 对象 obj。它必须是最初引用的对象,因为插入的 R 对象在插入 ArrayListJ 后可能已被其他逻辑更新。有人可以帮我实现 ArrayListX 或 ArrayListJ 吗?它基本上需要能够以恒定的时间将大量的R referenceclasses对象一个一个地追加到一个列表中(当列表变大时性能不应该变慢)。对于 ArrayListJ 实现,如何使用“get”方法取回最初引用的 R 对象?谢谢。

4

0 回答 0