为什么 R 函数在第一次 运行 时使用更多内存?
Why do R functions use more memory the first time they are run?
我一直在尝试比较矢量化 R 代码和非矢量化 R 代码,并注意到函数在第一次 运行 时似乎使用了更多内存。这是一个可重现的例子:
library(bench)
squares <- function(x)
{
y <- x
for(i in seq_along(x))
{
y[i] <- x[i]*x[i]
}
return(y)
}
x <- 1:100
bm <- mark(x^2, squares(x))
bm
这是第一次 运行,squares(x)
使用的内存(mem_alloc
列)比 x^2
:
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 0 558.1ns 1387977. 1.27KB 0 10000 0 7.21ms
2 squares(x) 12.4µs 14.2µs 64885. 4.15MB 0 10000 0 154.12ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
但是如果我再次 运行 代码,我会得到非常不同的结果:
> bm <- mark(x^2, squares(x))
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 0 490.1ns 1430864. 848B 0 10000 0 6.99ms
2 squares(x) 12.9µs 16.4µs 57321. 448B 5.73 9999 1 174.44ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
如果我再次 运行 基准测试,我会得到与第二次相同的结果。
如果,当我第一次启动 R 时,我 运行 基准测试之前的函数,我得到以下信息:
> 1^2
[1] 1
> squares(1)
[1] 1
> bm <- mark(x^2, squares(x))
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 0 977.1ns 993503. 1.27KB 0 10000 0 10.1ms
2 squares(x) 12.8µs 14.5µs 63713. 448B 0 10000 0 157ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
请注意,squares(x)
的内存使用率与第二个 运行 一样低,但 x^2
的内存使用率则不同。相反,如果我 运行 x^2
在第一个基准测试之前,用于 x^2
的内存下降到 848B
。
这是因为用于 R 的即时编译的内存在第一次函数 运行 时包含在内存分析中吗?如果是这样,为什么 x^2
会受到影响 - ^
运算符不是已经编译为字节码了吗?我是否误解了 R 中的内存分析功能?还是这里发生了其他事情?
根据 Roland 的评论(谢谢!),我尝试关闭 JIT 编译。结果表明,函数第一次使用额外的内存 运行 确实是由于编译:
library(compiler)
library(bench)
enableJIT(0) # Turn of JIT compilation
squares <- function(x)
{
y <- x
for(i in seq_along(x))
{
y[i] <- x[i]*x[i]
}
return(y)
}
结果如下:
> x <- 1:100
> bm <- mark(x^2, squares(x))
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 0 490.1ns 1205874. 1.27KB 0 10000 0 8.29ms
2 squares(x) 81.3µs 96.9µs 10428. 448B 62.7 4488 27 430.39ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
>
> # A second run:
> bm <- mark(x^2, squares(x))
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 0 559.1ns 1311094. 848B 0 10000 0 7.63ms
2 squares(x) 79.3µs 87.7µs 10749. 448B 80.0 4571 34 425.25ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
(不出所料,非编译函数速度较慢,尽管这不是实验的重点。)
此外,使用cmpfun
的显式编译也消除了第一个运行中多余的内存使用:
library(compiler)
squares <- cmpfun(squares)
产量
> x <- 1:100
> bm <- mark(x^2, squares(x))
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 1ns 560ns 1163023. 1.27KB 0 10000 0 8.6ms
2 squares(x) 11.1µs 13.5µs 71576. 448B 0 10000 0 139.7ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
第一个运行。
我一直在尝试比较矢量化 R 代码和非矢量化 R 代码,并注意到函数在第一次 运行 时似乎使用了更多内存。这是一个可重现的例子:
library(bench)
squares <- function(x)
{
y <- x
for(i in seq_along(x))
{
y[i] <- x[i]*x[i]
}
return(y)
}
x <- 1:100
bm <- mark(x^2, squares(x))
bm
这是第一次 运行,squares(x)
使用的内存(mem_alloc
列)比 x^2
:
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 0 558.1ns 1387977. 1.27KB 0 10000 0 7.21ms
2 squares(x) 12.4µs 14.2µs 64885. 4.15MB 0 10000 0 154.12ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
但是如果我再次 运行 代码,我会得到非常不同的结果:
> bm <- mark(x^2, squares(x))
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 0 490.1ns 1430864. 848B 0 10000 0 6.99ms
2 squares(x) 12.9µs 16.4µs 57321. 448B 5.73 9999 1 174.44ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
如果我再次 运行 基准测试,我会得到与第二次相同的结果。
如果,当我第一次启动 R 时,我 运行 基准测试之前的函数,我得到以下信息:
> 1^2
[1] 1
> squares(1)
[1] 1
> bm <- mark(x^2, squares(x))
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 0 977.1ns 993503. 1.27KB 0 10000 0 10.1ms
2 squares(x) 12.8µs 14.5µs 63713. 448B 0 10000 0 157ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
请注意,squares(x)
的内存使用率与第二个 运行 一样低,但 x^2
的内存使用率则不同。相反,如果我 运行 x^2
在第一个基准测试之前,用于 x^2
的内存下降到 848B
。
这是因为用于 R 的即时编译的内存在第一次函数 运行 时包含在内存分析中吗?如果是这样,为什么 x^2
会受到影响 - ^
运算符不是已经编译为字节码了吗?我是否误解了 R 中的内存分析功能?还是这里发生了其他事情?
根据 Roland 的评论(谢谢!),我尝试关闭 JIT 编译。结果表明,函数第一次使用额外的内存 运行 确实是由于编译:
library(compiler)
library(bench)
enableJIT(0) # Turn of JIT compilation
squares <- function(x)
{
y <- x
for(i in seq_along(x))
{
y[i] <- x[i]*x[i]
}
return(y)
}
结果如下:
> x <- 1:100
> bm <- mark(x^2, squares(x))
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 0 490.1ns 1205874. 1.27KB 0 10000 0 8.29ms
2 squares(x) 81.3µs 96.9µs 10428. 448B 62.7 4488 27 430.39ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
>
> # A second run:
> bm <- mark(x^2, squares(x))
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 0 559.1ns 1311094. 848B 0 10000 0 7.63ms
2 squares(x) 79.3µs 87.7µs 10749. 448B 80.0 4571 34 425.25ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
(不出所料,非编译函数速度较慢,尽管这不是实验的重点。)
此外,使用cmpfun
的显式编译也消除了第一个运行中多余的内存使用:
library(compiler)
squares <- cmpfun(squares)
产量
> x <- 1:100
> bm <- mark(x^2, squares(x))
> bm
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time
<bch:expr> <bch:> <bch:t> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm>
1 x^2 1ns 560ns 1163023. 1.27KB 0 10000 0 8.6ms
2 squares(x) 11.1µs 13.5µs 71576. 448B 0 10000 0 139.7ms
# … with 4 more variables: result <list>, memory <list>, time <list>, gc <list>
第一个运行。