测试 faster/slower 是 R 中的命令
Testing how faster/slower are commands in R
我想弄清楚fast/slow以下每个操作是如何计算从 1 到 100 的累计和的。
install.package('microbenchmark')
library(microbenchmark)
#Method 1
cs_for = function(x) {
for (i in x) {
if (i == 1) {
xc = x[i]
} else {
xc = c(xc, sum(x[1:i]))
}
}
xc
}
cs_for(1:100)
# Method 2: with apply (3 lines)
cs_apply = function(x) {
sapply(x, function(x) sum(1:x))
}
cs_apply(100)
# Method 3:
cumsum (1:100)
microbenchmark(cs_for(1:100), cs_apply(100), cumsum(1:100))
我得到的输出如下:
Unit: nanoseconds
expr min lq mean median uq max neval cld
cs_for(1:100) 97702 100551 106129.05 102500.5 105151 199801 100 c
cs_apply(100) 8700 9101 10639.99 9701.0 10651 54201 100 b
cumsum(1:100) 501 701 835.96 801.0 801 3201 100 a
这表明与其他两种方法相比,最后一种方法的效率排名最高。以防万一,我感兴趣的是每种方法的速度有多快(无论是百分比还是速度),我想知道您建议应用哪种操作?
您可以通过保存摘要来处理 table 个结果:
results <- summary(microbenchmark(cs_for(1:100), cs_apply(100), cumsum(1:100)))
然后直接进行计算,例如计算中位数时间大于最快中位数的次数:
results[,"median"]/min(results[,"median"])
# [1] 131.35714 14.14286 1.00000
请注意您的基准测试是否正确:cs_appy(100)
与 cs_for(100)
不同!遗憾的是“微基准测试”没有警告您这一点。相比之下,'bench' 包可以:
bench::mark(cs_for(1 : 100), cs_apply(100), cumsum(1 : 100))
# Error: Each result must equal the first result:
# `cs_for(1:100)` does not equal `cs_apply(100)`
请注意,您的基准测试具有代表性:您正在查看单个数据点(长度为 100 的输入),并且它是一个 tiny 输入大小。结果很可能受噪声影响,即使重复测量也是如此。
始终使用几种不同的输入大小进行基准测试,以了解渐近增长。事实上,你的问题“多少次”一种方法比另一种方法快甚至没有意义,因为不同的函数没有相同的渐近行为:两个是线性的,一个是二次的。要看到这一点,您 必须 反复测量。 “microbenchmark”不支持开箱即用,但“bench”支持(但你当然可以手动完成)。
要执行apples-to-apples比较,cumsum()
基准也应该包含在一个函数中,并且所有函数都应该接受相同的参数(我建议n
, 输入向量的大小).
将所有这些放在一起,并使用“bench”包,我们得到以下结果。我还使用“vapply”添加了一个基准测试,因为它通常优于 sapply
:
cs_for = function (n) {
x = 1 : n
for (i in x) {
if (i == 1L) {
xc = x[i]
} else {
xc = c(xc, sum(x[1 : i]))
}
}
xc
}
cs_apply = function (n) {
sapply(1 : n, \(x) sum(1 : x))
}
cs_vapply = function (n) {
vapply(1 : n, \(x) sum(1 : x), integer(1L))
}
cs_cumsum = function (n) {
cumsum(1 : n)
}
results = bench::press(
n = 10L ^ (1 : 4), # from 10 to 10,000
bench::mark(cs_for(n), cs_apply(n), cs_vapply(n), cs_cumsum(n))
)
由于数据科学的第一条规则是“可视化你的结果”,这里有一个情节:
results |>
mutate(label = reorder(map_chr(expression, deparse), desc(median))) |>
ggplot() +
aes(x = n, y = median, color = label) +
geom_line() +
geom_point() +
bench::scale_y_bench_time() +
scale_x_log10()
...如您所见,并非所有的线都线性增加。事实上,cs_for
和cs_cumsum
都增加了super-linearly。 cs_for
有 quadratic runtime 因为逐步附加到 xc
向量需要分配新存储并复制旧值。 apply
和 vapply
都避免了这种情况。当使用 for
by pre-allocating 你的结果向量时,你也可以避免这种情况。 cumsum
也 表现出 super-linear 运行时的事实实际上让我感到惊讶 - 它绝对不应该那样做!这值得进一步调查。
我想弄清楚fast/slow以下每个操作是如何计算从 1 到 100 的累计和的。
install.package('microbenchmark')
library(microbenchmark)
#Method 1
cs_for = function(x) {
for (i in x) {
if (i == 1) {
xc = x[i]
} else {
xc = c(xc, sum(x[1:i]))
}
}
xc
}
cs_for(1:100)
# Method 2: with apply (3 lines)
cs_apply = function(x) {
sapply(x, function(x) sum(1:x))
}
cs_apply(100)
# Method 3:
cumsum (1:100)
microbenchmark(cs_for(1:100), cs_apply(100), cumsum(1:100))
我得到的输出如下:
Unit: nanoseconds
expr min lq mean median uq max neval cld
cs_for(1:100) 97702 100551 106129.05 102500.5 105151 199801 100 c
cs_apply(100) 8700 9101 10639.99 9701.0 10651 54201 100 b
cumsum(1:100) 501 701 835.96 801.0 801 3201 100 a
这表明与其他两种方法相比,最后一种方法的效率排名最高。以防万一,我感兴趣的是每种方法的速度有多快(无论是百分比还是速度),我想知道您建议应用哪种操作?
您可以通过保存摘要来处理 table 个结果:
results <- summary(microbenchmark(cs_for(1:100), cs_apply(100), cumsum(1:100)))
然后直接进行计算,例如计算中位数时间大于最快中位数的次数:
results[,"median"]/min(results[,"median"])
# [1] 131.35714 14.14286 1.00000
请注意您的基准测试是否正确:
cs_appy(100)
与cs_for(100)
不同!遗憾的是“微基准测试”没有警告您这一点。相比之下,'bench' 包可以:bench::mark(cs_for(1 : 100), cs_apply(100), cumsum(1 : 100)) # Error: Each result must equal the first result: # `cs_for(1:100)` does not equal `cs_apply(100)`
请注意,您的基准测试具有代表性:您正在查看单个数据点(长度为 100 的输入),并且它是一个 tiny 输入大小。结果很可能受噪声影响,即使重复测量也是如此。
始终使用几种不同的输入大小进行基准测试,以了解渐近增长。事实上,你的问题“多少次”一种方法比另一种方法快甚至没有意义,因为不同的函数没有相同的渐近行为:两个是线性的,一个是二次的。要看到这一点,您 必须 反复测量。 “microbenchmark”不支持开箱即用,但“bench”支持(但你当然可以手动完成)。
要执行apples-to-apples比较,
cumsum()
基准也应该包含在一个函数中,并且所有函数都应该接受相同的参数(我建议n
, 输入向量的大小).
将所有这些放在一起,并使用“bench”包,我们得到以下结果。我还使用“vapply”添加了一个基准测试,因为它通常优于 sapply
:
cs_for = function (n) {
x = 1 : n
for (i in x) {
if (i == 1L) {
xc = x[i]
} else {
xc = c(xc, sum(x[1 : i]))
}
}
xc
}
cs_apply = function (n) {
sapply(1 : n, \(x) sum(1 : x))
}
cs_vapply = function (n) {
vapply(1 : n, \(x) sum(1 : x), integer(1L))
}
cs_cumsum = function (n) {
cumsum(1 : n)
}
results = bench::press(
n = 10L ^ (1 : 4), # from 10 to 10,000
bench::mark(cs_for(n), cs_apply(n), cs_vapply(n), cs_cumsum(n))
)
由于数据科学的第一条规则是“可视化你的结果”,这里有一个情节:
results |>
mutate(label = reorder(map_chr(expression, deparse), desc(median))) |>
ggplot() +
aes(x = n, y = median, color = label) +
geom_line() +
geom_point() +
bench::scale_y_bench_time() +
scale_x_log10()
...如您所见,并非所有的线都线性增加。事实上,cs_for
和cs_cumsum
都增加了super-linearly。 cs_for
有 quadratic runtime 因为逐步附加到 xc
向量需要分配新存储并复制旧值。 apply
和 vapply
都避免了这种情况。当使用 for
by pre-allocating 你的结果向量时,你也可以避免这种情况。 cumsum
也 表现出 super-linear 运行时的事实实际上让我感到惊讶 - 它绝对不应该那样做!这值得进一步调查。