挑战:优化退市 [容易]

Posted

技术标签:

【中文标题】挑战:优化退市 [容易]【英文标题】:challenge: optimize unlisting [easy] 【发布时间】:2011-04-30 02:12:25 【问题描述】:

因为 SO 最近有点慢,所以我发布了一个简单的问题。如果大鱼们留在替补席上并给新秀一个回应的机会,我将不胜感激。

有时我们的对象包含大量的大列表元素(向量)。您如何将此对象“取消列出”到单个向量中。证明您的方法比unlist() 更快。

【问题讨论】:

这里的每个人都是一条“大鱼”;)。您冒着得不到任何答案的风险。 大有多大?我们说的是鲑鱼、马林鱼还是鲸鲨? “大量的大型列表元素”是什么意思 - 长度为 1,000,000 或更长的向量?多少个列表元素是“荒谬”的数量? 必须保留向量的名称吗?如果是必须是唯一的?列表是递归的?默认的取消列表可以完成所有这些。 让我们说几个 10.000 列表元素,但我会让想象力变得疯狂。一般来说,元素的数量应该足够大,以显示速度性能的任何差异,但在内存限制范围内。名称可以转储。假设列表有 【参考方案1】:

如果你不需要名字并且你的名单只有一层,那么如果你能打败

.Internal(unlist(your_list, FALSE, FALSE))

我会投票赞成你在未来 1 年所做的一切!

[更新:如果需要非唯一名称并且列表不是递归的,这里有一个版本比 unlist 改进了 100 倍

 myunlist <- function(l)
    names <- names(l)
    vec <- unlist(l, F, F)
    reps <- unlist(lapply(l, length), F, F)
    names(vec) <- rep(names, reps)
    vec
    

 myunlist(list(a=1:3, b=2))
 a a a b 
 1 2 3 2 

 > tl <- list(a = 1:20000, b = 1:5000, c = 2:30)
 > system.time(for(i in 1:200) unlist(tl))
 user  system elapsed 
 22.97    0.00   23.00 

 > system.time(for(i in 1:200) myunlist(tl))
 user  system elapsed 
 0.2     0.0     0.2 

 > system.time(for(i in 1:200) unlist(tl, F, F))
 user  system elapsed 
 0.02    0.00    0.02 

]

[更新 2:回应 Richie Cotton 挑战 Nr3。

bigList3 <- replicate(500, rnorm(1e3), simplify = F)

unlist_vit <- function(l)
    names(l) <- NULL
    do.call(c, l)
    

library(rbenchmark)

benchmark(unlist = unlist(bigList3, FALSE, FALSE),
          rjc    = unlist_rjc(bigList3),
          vit    = unlist_vit(bigList3),
          order  = "elapsed",
          replications = 100,
          columns = c("test", "relative", "elapsed")
          )

    test  relative elapsed
1 unlist   1.0000    2.06
3    vit   1.4369    2.96
2    rjc   3.5146    7.24

]

PS:我认为“大鱼”是比你名声更大的鱼。所以我在这里很小:)。

【讨论】:

+1 这可能并不重要,但在我的测试中,您的版本比@ucfagls' 快一点。然而最大的加速来自 use.names=F。 我从 Roman 的问题中了解到,他希望用更智能的东西替换内置的“unlist”。就我而言,当不需要名称时,这是不可能的。 现在这是一个人无法拒绝的提议!如果我每天写 6 篇文章,我可能会抓住 Dirk。 FWIW,我认为有几条1000分的大鱼。 在我的系统上,unlist(bigList2, recursive = FALSE, use.names = FALSE).Internal(unlist(bigList2, FALSE, FALSE) 之间没有任何选择。在.Internal 调用之前unlist() 中的开销可以忽略不计。除非我真的非常确定我有正确的对象,否则我会尽量远离.Internal,以防万一,因为如果你出错或提供了函数没有预料到的东西,你可能会导致 R 崩溃。 @ucfagis ,这适用于大向量和少量迭代。但是,如果您的列表仅包含小向量并且您运行长时间的模拟,那么 .Internal 的改进可能会翻倍!!!【参考方案2】:

unlist() 的解决方案必须非常快才能击败unlist(),不是吗?在这里,用 2000 个长度为 100,000 的数字向量取消列出一个列表只需不到两秒的时间。

> bigList2 <- as.list(data.frame(matrix(rep(rnorm(1000000), times = 200), 
+                                       ncol = 2000)))
> print(object.size(bigList2), units = "Gb")
1.5 Gb
> system.time(foo <- unlist(bigList2, use.names = FALSE))
   user  system elapsed 
  1.897   0.000   2.019

在我的工作区中使用bigList2foo,R 正在使用我大约9Gb 的可用内存。密钥是use.names = FALSE。没有它unlist() 会非常缓慢。到底有多慢我还在等着发现......

我们可以通过设置recursive = FALSE 来加快速度,然后我们实际上与 VitoshKa 的答案相同(两个代表性时间):

> system.time(foo <- unlist(bigList2, recursive = FALSE, use.names = FALSE))
   user  system elapsed 
  1.379   0.001   1.416
> system.time(foo <- .Internal(unlist(bigList2, FALSE, FALSE)))
   user  system elapsed 
  1.335   0.000   1.344

...终于use.names = TRUE版本完成了...:

> system.time(foo <- unlist(bigList2, use = TRUE))
    user   system  elapsed 
2307.839   10.978 2335.815

它消耗了我所有系统 16Gb 的 RAM,所以我当时放弃了...

【讨论】:

【参考方案3】:

c() 具有逻辑参数recursive,当设置为TRUE(默认显然是FALSE)时,它将递归地取消列出向量。

l <- replicate(500, rnorm(1e3), simplify = F)

microbenchmark::microbenchmark(
  unlist = unlist(l, FALSE, FALSE),
  c = c(l, recursive = TRUE, use.names = FALSE)
)

# Unit: milliseconds
# expr      min       lq     mean   median       uq      max neval
# unlist 3.083424 3.121067 4.662491 3.172401 3.985668 27.35040   100
#      c 3.084890 3.133779 4.090520 3.201246 3.920646 33.22832   100

【讨论】:

【参考方案4】:

作为一条中等大小的鱼,我正在尝试第一次尝试的解决方案,该解决方案为小鱼提供了一个可以击败的基准。它比 unlist 慢大约 3 倍。

我正在使用ucfagls 的测试列表的较小版本。 (因为它更适合内存。)

bigList3 <- as.list(data.frame(matrix(rep(rnorm(1e5), times = 200), ncol = 2000)))

基本思想是创建一个长向量来存储答案,然后遍历列表项并从列表中复制值。

unlist_rjc <- function(l)

  lengths <- vapply(l, length, FUN.VALUE = numeric(1), USE.NAMES = FALSE)
  total_len <- sum(lengths)
  end_index <- cumsum(lengths)
  start_index <- 1 + c(0, end_index)
  v <- numeric(total_len)
  for(i in seq_along(l))
  
    v[start_index[i]:end_index[i]] <- l[[i]]
  
  v


t1 <- system.time(for(i in 1:10) unlist(bigList2, FALSE, FALSE))
t2 <- system.time(for(i in 1:10) unlist_rjc(bigList2))
t2["user.self"] / t1["user.self"]  # 3.08

小鱼儿的挑战: 1. 可以扩展它来处理数字以外的其他类型吗? 2. 你能让它与递归(嵌套列表)一起工作吗? 3. 能不能快点?

如果答案符合这些小挑战中的一项或多项,我会投票给得分低于我的人。

【讨论】:

中型?根据ranking of top users in R tag,您是过去 30 天内的第 10 位回答者,并且是所有时间的第 7 位;) 哇。没有意识到这一点。耶我。无论如何,我的提议仍然有效。改进/击败我的答案 -> 获得支持。 在我的机器上你的东西慢了大约 5 倍。 干净重启后,它只慢了 3.5 倍。我为您的挑战 nr3 更新了我的答案。等待投票:)

以上是关于挑战:优化退市 [容易]的主要内容,如果未能解决你的问题,请参考以下文章

PySpark UDF 优化挑战

音视频业务挑战与优化实践,MXPlayer在线播放优化,数据分析与播放器优化

唯品会HDFS性能挑战和优化实践

滴滴全线业务优化,芭比Q 了?

挑战杯创业计划书_基于云计算的下料优化软件_1. 执行总结

唯品会HDFS性能挑战和优化实践