Jul*_*ien 5 performance r vectorization
u <- rnorm(10000)
v <- rnorm(10000)
# `outer`
system.time(mat1 <- outer(u, v , `<`))
# user system elapsed
# 1.80 1.34 3.32
# `for` loop
system.time({
mat2 <- matrix(NA, nrow = length(u), ncol = length(v))
for(i in seq_along(v)) {
mat2[, i] <- u < v[i]
}
})
# user system elapsed
# 0.97 0.02 1.01
identical(mat1, mat2)
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)
如果您使用bench::press()列出的四个选项,您可以感觉到内存分配最多的方法花费的时间最长,正如 David Arenburg 在评论中所建议的那样。
这四个选项是:
outer()。for循环。vapply()(来自 sindri_baldur 的评论)。 `<`(rep(x), rep(y))(正是outer()在幕后所做的事情)。我喜欢bench,因为它显示内存使用情况。该图中的每个方面都通过矩阵显示了这四种方法的速度n*n以及垃圾收集的级别。
对于 100 行,比其他方法慢,并且(垃圾收集)vapply没有区别。gc
然而,一旦数据大于此值,我们可以看到vapply()垃圾收集次数减少了很多,而且速度也快了很多。
类似地,在最后一个方面(1e4行和列),我们可以看到for循环的垃圾收集更少,并且往往比outer().
vapply()使用最少的 RAM您可能怀疑vapply()垃圾收集较少,因为它留下了更多垃圾未收集。但是,如果我们查看 RAM 总使用量,我们可以看到它实际上使用了大约三分之一的 RAM outer():
注意:我不知道它如何实际使用 0 字节来创建 1x1 矩阵 - 但如果你真的比较两个标量,你可能根本不会使用矩阵。
请参阅 R 内部章节,写屏障和垃圾收集器:
收藏分为三个级别。0 级仅收集最年轻的一代,1 级收集两个最年轻的代,2 级收集所有代。20 次 0 级收集后,下一次收集位于 1 级,5 次 1 级收集后,下一次收集位于 2 级。此外,如果 n 级收集无法提供 20% 的可用空间(对于每个节点和向量堆) ,下一个集合将位于 n+1 层。(R 级函数 gc() 执行 2 级收集。)
理解这一点的方法是,如果一个函数创建更多临时对象,然后销毁它们,它将执行更多分配并进行更多垃圾收集。
sizes <- c(1, 1e2, 1e3, 1e4)
results <- bench::press(
size = sizes,
{
set.seed(1)
u <- rnorm(size)
v <- rnorm(size)
bench::mark(
min_iterations = 10,
check = FALSE,
outer = {
mat <- outer(u, v, `<`)
},
loop = {
mat <- matrix(NA, nrow = length(u), ncol = length(v))
for (i in seq_along(v)) {
mat[, i] <- u < v[i]
}
mat
},
vapply = {
mat <- vapply(seq_along(v), \(i) u < v[i], logical(length(u)))
},
seq = {
mat <- as.matrix(
`<`(
rep(u, times = ceiling(length(v) / length(u))),
rep(v, rep.int(length(u), length(v)))
),
nrow = length(u)
)
}
)
}
)
ggplot2::autoplot(results) +
ggplot2::facet_wrap(ggplot2::vars(size),scales="free_x")
Run Code Online (Sandbox Code Playgroud)
library(ggplot2)
p <- results |>
dplyr::mutate(
expr = attr(expression, "description"),
size = as.factor(size)) |>
ggplot() +
geom_col(aes(
x = reorder(expr, mem_alloc),
y = mem_alloc,
fill = size
), color= "black") +
facet_wrap(vars(size), scales="free_y") +
labs(
title = "Total RAM usage",
y = "Bytes",
x = "Expression"
)
Run Code Online (Sandbox Code Playgroud)
免责声明:这些是在一台机器(一台不起眼的相当旧的笔记本电脑)上的结果。我没有得到outer()与for您一样的循环之间的相同程度的差异,因此您的结果可能会有所不同。
| 归档时间: |
|
| 查看次数: |
245 次 |
| 最近记录: |