【问题标题】:forcing modify in place in an R6 class (in R)强制在 R6 类中进行修改(在 R 中)
【发布时间】:2018-04-11 14:29:23
【问题描述】:

所以我正在使用 R6(我老板的偏好)在 R 中编写一个程序。它必须做一些繁重的数字运算,所以我试图让 R6 类中的关键变量就地修改。不幸的是,在普通 R 中使变量修改到位的方法似乎在 R6 类中不起作用。我在下面构建了一个最小的示例。您可以清楚地看到 R6 类内部的变量,该变量在函数之后跳转到新的内存地址。在 R6 类之外,做完全相同的事情不会导致复制。谁能给我任何建议,说明为什么以及如何让类中的变量就地修改?

my_r6 <- R6Class("my_r6",
  public = list(
    test = function() {
      for (i in 1:5) {
        private$x$a[i] <- 3
      }
    }
  ),
  private = list(
    x = list(a = c(1, 2, 3, 4, 5))
  )
)
temp_r6 <- my_r6$new()
tracemem(temp_r6$.__enclos_env__$private$x$a)
temp_r6$test()
y <- list(b = c(1, 2, 3, 4, 5))
tracemem(y$b)
for (i in 1:5) {
  y$b[i] <- 3
}

【问题讨论】:

  • 为什么你的代码要关心是否有修改到位?
  • 因为它是高性能计算蒙蒂卡罗模拟的一部分。就地修改在大型数据结构上要快得多。如果您想知道为什么我们要尝试在 R 中而不是 C 中进行高性能计算,请参阅之前关于我老板的评论。

标签: r r6


【解决方案1】:

您的代码并未测试您认为的内容。 tracemem() 不会告诉您对象是否已就地修改。 ?tracememDetails 部分表示它会告诉您 C 函数 duplicate() 是否已被调用。

并且(仅?)当多个名称/符号引用相同的数据时会发生这种情况。例如:

y <- list(b=1:5)
tracemem(y)
# [1] "<0000000009226B08>"
y[1] <- pi/2  # nothing else points to the same memory as 'y'
z <- y        # now 'z' and 'y' point to the same memory
z[1] <- pi    # this requires duplication of y
# tracemem[0x0000000009226b08 -> 0x00000000092228c8]:

此外,您在 R6 类之外的代码与其内部的代码不同。 private 对象是一个环境,因此更准确的比较如下。您可以看到它显示了与 R6 代码相同的重复项。

e <- new.env()
e$y <- list(b = c(1, 2, 3, 4, 5))
tracemem(e$y$b)
for (i in 1:5) {
  e$y$b[i] <- 3
}
# tracemem[0x0000000008ff9c78 -> 0x000000000dac2298]:

我不会花时间调查或解释重复发生的原因或避免重复的方法,以防万一这是XY problem。如果这与您的实际问题非常接近,请告诉我,我会尝试通过编辑来回答。

【讨论】:

  • 我可能是错的,但我对 tracemem 的理解是它跟踪变量从一个 ram 块到另一个的复制操作。如果变量被“就地修改”,则不会有副本,原始的 ram 块将被覆盖。我确实尝试使用 pryr 库中的地址函数来跟踪它,但 pryr 似乎将 R6 对象中的所有变量都视为具有相同的地址。我确实需要一种方法来跟踪 R 何时复制的原子数据类型并停止它在程序的关键部分执行此操作......所以不是 X/Y 问题。
  • @PeterClark:你对tracemem()的理解是错误的。它只跟踪通过调用duplicate() 发生的复制(如帮助页面中所述)。它不跟踪其他类型的分配/复制。 R 非常努力地维护按值传递的语义,因此通过引用 R 来修改原子类型通常非常困难。也就是说,通过 C API 很容易做到这一点,但有关于使用可变数据结构的所有警告。
  • 不幸的是,有人要求我提供不涉及编译任何 c 代码的解决方案。除非有一种直接从 R 调用 C API 的方法对我没有多大帮助?就个人而言,我宁愿用 C 编写整个项目,但这不是一个选择。
  • 您对实际问题的描述似乎是一个目标和约束,具有基本上不可行的解决方案。如果我处于你的位置,我会重新审视能够通过引用修改 R 对象的要求。我仍然认为这可能是一个 XY 问题,因为您没有证明您存在由类似于您设计的示例的内存(重新)分配引起的性能瓶颈。
  • 是的,在代码基本完成之前,我无法证明这一点。在这一点上,我可能需要废弃大量代码。所以这基本上就是我正在做的事情。也许如果我向他们展示完成的程序遭受内存溢出或性能非常慢的影响,他们会让我重新用 c 编写。我宁愿不必先完成 r 代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多