【问题标题】:The fastest method to point an element in a nested list在嵌套列表中指向元素的最快方法
【发布时间】:2021-12-26 15:32:56
【问题描述】:

假设我们有一个任意大小的大型嵌套列表(深度级别可能超过 100)。该列表包含列表中非预定义位置的对象(最多数千个),我们需要经常查看和修改这些对象。因此,我们在列表中保留一个带有指向这些对象的指针的单独变量。我们需要知道创建指针的最快方法是什么。

到目前为止,我可以用下面的代码想到 4 种不同的解决方案:

首先,为了演示,我们需要一个虚拟的嵌套列表对象:

create_nested_list <- function(depth) {
     myList = list()
     if(depth > 0) {
    depth <- depth - 1
    myList$level <- paste0(paste0('depth_', depth))
    myList[[paste0('Depth_', depth, '_A')]] <- create_nested_list(depth)
    myList[[paste0('Depth_', depth, '_B')]] <- create_nested_list(depth)   }
     return(myList) }

myList <- create_nested_list(10)

然后,假设我们希望看到并经常修改列表中的以下属性:

myList$Depth_9_B$Depth_8_A$Depth_7_A$Depth_6_A$Depth_5_A$Depth_4_A$Depth_3_A$Depth_2_A$level
  1. 上面的表达式将是访问列表中元素的直接方法。但是,它不适用于我们的情况,因为上面的代码会创建对象的副本而不是指针。

  2. Base-R 解决方案是将对象的路径保存在字符串中并计算表达式。

path <- '$Depth_9_B$Depth_8_A$Depth_7_A$Depth_6_A$Depth_5_A$Depth_4_A$Depth_3_A$Depth_2_A$level'
eval(str2lang(paste0('myList', path))) 
  1. 我们也可以使用库“pointr”来创建指针对象。
library(pointr)
ptr('pointer_to_the_object', 'myList$Depth_9_B$Depth_8_A$Depth_7_A$Depth_6_A$Depth_5_A$Depth_4_A$Depth_3_A$Depth_2_A$level')
pointer_to_the_object
  1. 我们可以使用 R6/Reference 类,而不是使用 S3 类对象。但在这种情况下,列表中的每个元素都必须是一个单独的 S6 类对象。我们需要改变创建基本列表的方式。

     library(R6)
     nestedR6 <- R6Class(
       'myList',
       cloneable = FALSE,
       lock_objects = FALSE,
       public = list(
         ref_list = NULL,
         initialize = function(depth) {
    
           if(depth > 0) {
             depth <- depth - 1
             self$level <- paste0(paste0('depth_', depth))
             self[[paste0('Depth_', depth, '_A')]] <- nestedR6$new(depth)
             self[[paste0('Depth_', depth, '_B')]] <- nestedR6$new(depth)
           }
         }
       )
     )
    
myListR6 <- nestedR6$new(10)
R6obj <- myListR6$Depth_9_B$Depth_8_A$Depth_7_A$Depth_6_A$Depth_5_A$Depth_4_A$Depth_3_A$Depth_2_A

然后,我们可以比较所有 4 种方法的速度:

library(microbenchmark)
library(ggplot2)

mbm <- microbenchmark(direct = myList$Depth_9_B$Depth_8_A$Depth_7_A$Depth_6_A$Depth_5_A$Depth_4_A$Depth_3_A$Depth_2_A$level,
               direct2 = myList[['Depth_9_B']][['Depth_8_A']][['Depth_7_A']][['Depth_6_A']][['Depth_5_A']][['Depth_4_A']][['Depth_3_A']][['Depth_2_A']][['level']],
               eval_expression = {
                 eval(str2lang(paste0('myList', path)))
               }, 
               pointer = pointer_to_the_object,
               R6_Class = R6obj[['level']],
               times = 100)
autoplot(mbm)

令人惊讶的是,通过指针对象的访问是最慢的,而 R6 类指针的工作速度甚至比直接访问还要快。不幸的是,R6 类并不是最佳解决方案,因为通过 R6 对象创建嵌套列表比 S3 慢得多。

microbenchmark(
  S3 = create_nested_list(10),
  S6 = nestedR6$new(10)
)

【问题讨论】:

    标签: r pointers r6


    【解决方案1】:

    我找到了解决办法。

    我们可以使用环境对象(基本上是无特征的 R6 类),而不是使用列表。

    ## Create the environment object (almost the same as list)
    create_nested_environment <- function(depth) {
      
      myList = new.env()
      
      if(depth > 0) {
        depth <- depth - 1
        myList$level <- paste0(paste0('depth_', depth))
        myList[[paste0('Depth_', depth, '_A')]] <- create_nested_environment(depth)
        myList[[paste0('Depth_', depth, '_B')]] <- create_nested_environment(depth)
      }
      
      return(myList)
    }
    
    myList <- create_nested_list(10)
    
    myEnv <- create_nested_environment(10)
    e <- myEnv$Depth_9_B$Depth_8_A$Depth_7_A$Depth_6_A$Depth_5_A$Depth_4_A$Depth_3_A$Depth_2_A
    

    现在我们可以测试所有方法的性能了:

    mbm <- microbenchmark(direct = myList$Depth_9_B$Depth_8_A$Depth_7_A$Depth_6_A$Depth_5_A$Depth_4_A$Depth_3_A$Depth_2_A$level,
                          direct2 = myList[['Depth_9_B']][['Depth_8_A']][['Depth_7_A']][['Depth_6_A']][['Depth_5_A']][['Depth_4_A']][['Depth_3_A']][['Depth_2_A']][['level']],
                          eval_expression = {
                            eval(str2lang(paste0('myList', path)))
                          }, 
                          pointer = pointer_to_the_object,
                          R6_Class = R6obj[['level']],
                          env = e[['level']],
                          env_direct = myEnv$Depth_9_B$Depth_8_A$Depth_7_A$Depth_6_A$Depth_5_A$Depth_4_A$Depth_3_A$Depth_2_A$level,
                          times = 1000)
    autoplot(mbm)
    

    指向环境对象的指针甚至比列表中的直接链接或 R6 类中的指针更快 [

    以及列表、R6 类和原始环境对象之间的速度比较:

    mbm <- microbenchmark(
      S3 = create_nested_list(10),
      R6 = nestedR6$new(10),
      env = create_nested_environment(10)
    )
    autopilot(mbm)
    

    与列表的性能相同。

    【讨论】:

      猜你喜欢
      • 2016-12-17
      • 2011-06-01
      • 2017-05-26
      • 1970-01-01
      • 1970-01-01
      • 2011-08-06
      • 2016-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多