R 为什么预分配对列表有用?
我理解预先分配向量或矩阵是很有用的,因为它们总是存储在连续的内存块中 但是,就列表而言,它可以包含不同长度和模式的元素。因此,我的第一个猜测是,列表可能只包含指向其元素真实地址的指针。我说得对吗?这里有一个相关的问题,它说列表本质上是一个数组,但它不包括元素如何存储在列表中,而每个元素的大小可能会改变R 为什么预分配对列表有用?,r,R,我理解预先分配向量或矩阵是很有用的,因为它们总是存储在连续的内存块中 但是,就列表而言,它可以包含不同长度和模式的元素。因此,我的第一个猜测是,列表可能只包含指向其元素真实地址的指针。我说得对吗?这里有一个相关的问题,它说列表本质上是一个数组,但它不包括元素如何存储在列表中,而每个元素的大小可能会改变 示例1:如果列表包含a、b、c、d、e,并且myList$asystem.time({myList这对于注释来说太长了,但不是完整的答案 在修改时的复制方面,命名列表与未命名列表的处理方式不同 复
示例1:如果列表包含
a、b、c、d、e
,并且myList$asystem.time({myList这对于注释来说太长了,但不是完整的答案
在修改时的复制方面,命名列表与未命名列表的处理方式不同
复制(用于大型对象)
这是一个复杂的问题。请参阅以获得合理的解释,并注意其中一些更改正在R的开发版本中实现。另请参阅以获得对底层复杂性的更多理解
以下是各种预分配可能性的一些时间安排
# preallocated contents so timing is list related only
.a <- seq_len(1e6); .b <- seq_len((1e6 + 1))
.c <- seq_len((1e6 + 2)); .d <- seq_len((1e6 + 3))
f1 <- function(){
# using `$<-` empty list
x <- list()
x$a <- .a
x$b <- .b
x$c <- .c
x$d <- .d
x
}
f2 <- function(){
# using `[[<-` on empty list
x <- list()
x[['a']] <- .a
x[['b']] <- .b
x[['c']] <- .c
x[['d']] <- .d
x
}
f3 <- function(){
# using `[[<-` on empty list, naming later
x <- list()
x[[1]] <- .a
x[[2]] <- .b
x[[3]] <- .c
x[[4]] <- .d
names(x) <- letters[1:4]
x
}
f4 <- function(){
# just define the list
x <- list(a = .a, b = .b,
c = .c, d = .d)
}
f5 <- function(){
# create a list of length 4, then fill and name
x <- vector(mode = 'list', length = 4)
x[[1]] <- .a
x[[2]] <- .b
x[[3]] <- .c
x[[4]] <- .d
names(x) <- letters[1:4]
x
}
# f6 <- function(){
# # this doesn't work!
# # it creates a list of length 8
# x <- vector(mode = 'list', length = 4)
# x[['a']] <- .a
# x[['b']] <- .b
# x[['c']] <- .c
# x[['d']] <- .d
# x
# }
f7 <-function(){
# pre allocate list, name then fill
x <- vector(mode = 'list', length = 4)
names(x) <- letters[1:4]
x[[1]] <- .a
x[[2]] <- .b
x[[3]] <- .c
x[[4]] <- .d
x
}
f8 <- function(){
# preallocate correct length and then name
# and fill by name
x <- vector(mode = 'list', length = 4)
names(x) <- letters[1:4]
x[['a']] <- .a
x[['b']] <- .b
x[['c']] <- .c
x[['d']] <- .d
x
}
library(microbenchmark)
microbenchmark(f1(),f2(),f3(),f4(),f5(),f7(),f8(),times=100)
microbenchmark(f1(),f2(),f3(),f4(),f5(),f7(),f8(),times=100)
# Unit: microseconds
# expr min lq median uq max neval
# f1() 6.038 11.169 12.980 14.791 34.110 100
# f2() 2528.881 4387.962 4707.014 6472.823 74586.266 100
# f3() 2532.805 4537.376 4714.258 5353.722 74903.206 100
# f4() 2475.756 4531.489 4721.503 6331.860 74460.395 100
# f5() 2508.959 4512.474 4759.535 6673.551 7966.668 100
# f7() 2545.181 4477.761 4709.127 6372.610 7964.856 100
# f8() 2508.053 4467.799 4669.131 6181.993 74236.726 100
#
# All results are identical.
all(identical(f1(),f2()),identical(f1(),f3()),
identical(f1(),f4()),identical(f1(),f5()),
identical(f1(),f7()),identical(f1(),f8()))
#预先分配的内容,因此计时仅与列表相关
。如果您确实需要存储指向您应该使用的环境而不是列表的元素的指针,则可能与问题的旁注重复。这可能很有用:以下参考提供了有关内存分析的详细信息,以查看发生的情况:。我无法在其他计算机上复制,我认为exaPLE可能太小,无法显示真正的差异。R可能会有效地预先分配一些叶子,只有当您超过该数量时,优势才会显现。在这种情况下,时间安排可能会被评估和分配的其他方面所压倒。我想您可能想要预分配50个元素的ls将一个列表扩展到50个元素,看看是否有差异。好吧,我来自C#和F#的世界。但我必须在工作中使用R。我对R了解得越多,我就越讨厌R。R需要花很多时间来适应。我发现,如果我更像是一种工具而不是一种精心设计的语言来处理它,我就不会我在2017年重新运行了这段代码,结果与R3.4.0完全不同,最快的是f4(),然后f1,5,6是相同的,安静的快,但没有f4快,然后是f8,f3,f2最慢
> system.time( { myList <- vector("list", 4)
+ myList[[1]] <- 1:10000000
+ myList[[2]] <- 1:10000100
+ myList[[3]] <- 1:10000200
+ myList[[4]] <- 1:10000300
+ names(myList) <- letters[1:4]})
user system elapsed
0.01 0.02 0.03
system.time( { myList <- list()
myList$a <- 1:100000
myList$b <- 1:100001
myList$c <- 1:100002
myList$d <- 1:100003})
# user system elapsed
# 0.019 0.001 0.022
system.time({ myList<-list(a=1:100000,b=1:100001,c=1:100002,d=1:100003) })
# user system elapsed
# 0.001 0.001 0.002
system.time( { myList <- vector("list", 4)
myList[[1]] <- 1:100000
myList[[2]] <- 1:100001
myList[[3]] <- 1:100002
myList[[4]] <- 1:100003
names(myList) <- letters[1:4]})
# user system elapsed
# 0.001 0.001 0.001
# preallocated contents so timing is list related only
.a <- seq_len(1e6); .b <- seq_len((1e6 + 1))
.c <- seq_len((1e6 + 2)); .d <- seq_len((1e6 + 3))
f1 <- function(){
# using `$<-` empty list
x <- list()
x$a <- .a
x$b <- .b
x$c <- .c
x$d <- .d
x
}
f2 <- function(){
# using `[[<-` on empty list
x <- list()
x[['a']] <- .a
x[['b']] <- .b
x[['c']] <- .c
x[['d']] <- .d
x
}
f3 <- function(){
# using `[[<-` on empty list, naming later
x <- list()
x[[1]] <- .a
x[[2]] <- .b
x[[3]] <- .c
x[[4]] <- .d
names(x) <- letters[1:4]
x
}
f4 <- function(){
# just define the list
x <- list(a = .a, b = .b,
c = .c, d = .d)
}
f5 <- function(){
# create a list of length 4, then fill and name
x <- vector(mode = 'list', length = 4)
x[[1]] <- .a
x[[2]] <- .b
x[[3]] <- .c
x[[4]] <- .d
names(x) <- letters[1:4]
x
}
# f6 <- function(){
# # this doesn't work!
# # it creates a list of length 8
# x <- vector(mode = 'list', length = 4)
# x[['a']] <- .a
# x[['b']] <- .b
# x[['c']] <- .c
# x[['d']] <- .d
# x
# }
f7 <-function(){
# pre allocate list, name then fill
x <- vector(mode = 'list', length = 4)
names(x) <- letters[1:4]
x[[1]] <- .a
x[[2]] <- .b
x[[3]] <- .c
x[[4]] <- .d
x
}
f8 <- function(){
# preallocate correct length and then name
# and fill by name
x <- vector(mode = 'list', length = 4)
names(x) <- letters[1:4]
x[['a']] <- .a
x[['b']] <- .b
x[['c']] <- .c
x[['d']] <- .d
x
}
library(microbenchmark)
microbenchmark(f1(),f2(),f3(),f4(),f5(),f7(),f8(),times=100)
microbenchmark(f1(),f2(),f3(),f4(),f5(),f7(),f8(),times=100)
# Unit: microseconds
# expr min lq median uq max neval
# f1() 6.038 11.169 12.980 14.791 34.110 100
# f2() 2528.881 4387.962 4707.014 6472.823 74586.266 100
# f3() 2532.805 4537.376 4714.258 5353.722 74903.206 100
# f4() 2475.756 4531.489 4721.503 6331.860 74460.395 100
# f5() 2508.959 4512.474 4759.535 6673.551 7966.668 100
# f7() 2545.181 4477.761 4709.127 6372.610 7964.856 100
# f8() 2508.053 4467.799 4669.131 6181.993 74236.726 100
#
# All results are identical.
all(identical(f1(),f2()),identical(f1(),f3()),
identical(f1(),f4()),identical(f1(),f5()),
identical(f1(),f7()),identical(f1(),f8()))