提问人:WishIaskedThisSooner 提问时间:10/28/2023 最后编辑:jay.sfWishIaskedThisSooner 更新时间:10/28/2023 访问量:38
基准测试:l应用速度慢一个数量级以上。为什么?
Benchmarked: lapply slower by more than an order of magnitude. Why?
问:
我担心我的代码自由使用(和)可能会慢一个数量级以上。lapply
sapply
例如:以以下列表为例,它反映了我的数据。它很小,仅用于说明目的,但请相信我,在处理数百万个数据点时,速度会变慢。ls
lapply
set.seed(0)
dates <- as.character(seq.Date(as.Date('2023-01-01'), by = 'day', length.out = 365))
m <- sapply(dates, \(d) sample(1:10, 100, replace = TRUE))
rownames(m) <- seq(1001, 1100, 1)
ls <- list()
ls <- sapply(dates, \(d) {ls[[length(ls) + 1]] <- m[, d]; ls})
str(ls[1:3])
List of 3
$ 2023-01-01: Named int [1:100] 9 4 7 1 2 7 2 3 1 5 ...
..- attr(*, "names")= chr [1:100] "1001" "1002" "1003" "1004" ...
$ 2023-01-02: Named int [1:100] 3 10 3 1 6 6 4 9 5 1 ...
..- attr(*, "names")= chr [1:100] "1001" "1002" "1003" "1004" ...
$ 2023-01-03: Named int [1:100] 1 10 4 9 9 9 9 6 6 4 ...
..- attr(*, "names")= chr [1:100] "1001" "1002" "1003" "1004" ...
目标很简单:计算每个元素 x 的 MAX(c(5 - x, 0)),其中有 36,500 个数据点。至少有四种不同的方法可以做到这一点,它们在下面进行了基准测试。ls
library(bench)
bench::mark(a = {
new <- sapply(dates, \(d)
lapply(rownames(m), \(n) max(c(5 - ls[[d]][n], 0))))
dimnames(new)[[1]] <- rownames(m)
new |> lapply(sum) |> unlist() |> sum() # Checksum
},
b = {
new <- sapply(ls, \(x) pmax(5 - x, 0))
new |> lapply(sum) |> unlist() |> sum() # Checksum
},
c = {
new <- do.call(cbind, ls)
new <- pmax(5 - new, 0)
new |> lapply(sum) |> unlist() |> sum() # Checksum
},
d = {
new <- 5 - do.call(cbind, ls)
new[new < 0] <- 0
new |> lapply(sum) |> unlist() |> sum() # Checksum
})
# A tibble: 4 × 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time result memory time
<bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm> <list> <list> <list>
1 a 363.3ms 366.1ms 2.73 1.44MB 2.73 2 2 732ms <dbl> <Rprofmem> <bench_tm>
2 b 26.1ms 32.2ms 26.5 2MB 9.48 14 5 527ms <dbl> <Rprofmem> <bench_tm>
3 c 24.1ms 26.4ms 33.5 1.53MB 7.44 18 4 538ms <dbl> <Rprofmem> <bench_tm>
4 d 23.7ms 26.3ms 21.0 1.6MB 5.25 12 3 572ms <dbl> <Rprofmem> <bench_tm>
为什么和其余的之间有超过一个数量级的差异?这是一个足够简单的计算。这是否意味着在使用时必须防范隐藏的陷阱?如果是这样,它们是什么?a
lapply
编辑在 GuedesBF 提供一些建设性的反馈后,我已经清理了我的代码以进行基准测试,这加快了速度,但结果实际上更糟,有近 2 个数量级的差异,哎呀!编写清晰高效的代码是 R 战斗的 90%,而查找教程很困难,这就是我在这里的原因。因此,感谢所有贡献者。
有一点很清楚,混乱的嵌套是问题的一部分,在最终的函数定义中指定了迭代,而不是像现在这样在前面干净地指定。lapply
bench::mark(a = {new <- sapply(ls, \(d)
lapply(d, \(x) max(c(5 - x, 0))))
new |> unlist() |> sum()}, # Checksum
b = {new <- sapply(ls, \(x) pmax(5 - x, 0))
new |> unlist() |> sum()}, # Checksum
c = {new <- do.call(cbind, ls)
new <- pmax(5 - new, 0)
new |> unlist() |> sum()}, # Checksum
d = {new <- 5 - do.call(cbind, ls)
new[new < 0] <- 0
new |> unlist() |> sum()}) # Checksum
# A tibble: 4 × 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time result memory time
<bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm> <list> <list> <list>
1 a 60.96ms 62.2ms 16.0 1.16MB 5.34 6 2 375ms <dbl> <Rprofmem> <bench_tm>
2 b 7.64ms 8.3ms 118. 1.16MB 4.39 54 2 456ms <dbl> <Rprofmem> <bench_tm>
3 c 621.7µs 818µs 1236. 715.93KB 7.17 517 3 418ms <dbl> <Rprofmem> <bench_tm>
4 d 627.5µs 803.6µs 1218. 787.13KB 7.35 497 3 408ms <dbl> <Rprofmem> <bench_tm>
另外,我在想,如果加速只是由于 ,并且没有类似的功能可用,例如 、 等怎么办?好吧,似乎列绑定而不是使用总是更快。以下是一些基准测试结果,其中仍然有近 2 个数量级的差异。pmax
mean
sqrt
lapply
sqrt
bench::mark(a = {new <- sapply(ls, \(d)
lapply(d, \(x) sqrt(x)))
new |> unlist() |> sum()},
b = {new <- do.call(cbind, ls)
new <- sqrt(new)
new |> unlist() |> sum()})
# A tibble: 2 × 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time result memory time
<bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm> <list> <list> <list>
1 a 32.6ms 35.6ms 28.0 1.15MB 5.09 11 2 393ms <dbl> <Rprofmem> <bench_tm>
2 b 475.9µs 570µs 1713. 430.73KB 6.82 754 3 440ms <dbl> <Rprofmem> <bench_tm>
答: 暂无答案
评论