在 while 循环中停止 R 中的修改时复制行为
Stop copy-on-modify behavior in R in a while loop
我正在对 R 进行拒绝抽样,需要尽可能高效。这是我的原始代码:
N <- 1e8
x <- rexp(N, 3) + rexp(N, 3)
todo <- runif(N, -1, 1) < cos(3.2*pi*x)
while(any(todo)){
x[todo] <- rexp(sum(todo), 3) + rexp(sum(todo), 3)
todo[todo] <- runif(sum(todo), -1, 1) < cos(3.2*pi*x[todo])
}
我在 https://adv-r.hadley.nz/names-values.html 上阅读有关复制和修改的内容,并决定使用 lobstr 包来查找是否有任何相关对象(使用 tracemem()
函数),并且 lo-看哪,逻辑向量 todo
在 while 循环中不断被复制!
如果我做以下测试:
library(lobstr)
N <- 1e8
x <- rexp(N, 3) + rexp(N, 3)
todo <- runif(N, -1, 1) < cos(3.2*pi*x)
cat(tracemem(todo), "\n")
while(any(todo)){
x[todo] <- rexp(sum(todo), 3) + rexp(sum(todo), 3)
todo[todo] <- runif(sum(todo), -1, 1) < cos(3.2*pi*x[todo])
}
我得到以下结果(证实了我的担忧):
[1] "0x38f4938"
[1] "0x1959ff30"
[1] "0x38f4938"
[1] "0x173bb6f0"
[1] "0x38f4938"
[1] "0x4caf788"
[1] "0x38f4938"
[1] "0x1a801628"
[1] "0x38f4938"
[1] "0x18f36768"
[1] "0x38f4938"
[1] "0x4e4d478"
[1] "0x38f4938"
[1] "0x195b93d8"
[1] "0x38f4938"
[1] "0x3f59fe0"
[1] "0x38f4938"
[1] "0x45ebf40"
[1] "0x38f4938"
[1] "0x1a42bdd8"
[1] "0x38f4938"
[1] "0x16c72ba0"
有人可以帮我摆脱这种耗时的行为吗?我尝试了以下声明,但没有成功:
todo <- logical(N)
todo <- list(logical(N))
编辑:另外,如果能帮助我提高代码中这个瓶颈的时间效率,我们将不胜感激...
我认为 base R 不可能做到这一点,但您可以使用 data.table
包优化此过程。试试这个,你会看到没有复制(我还做了一些其他的小改动来进一步优化你的代码)
library(data.table)
N <- 1e7; i <- 1:N
dt <- list(x = double(N), todo = logical(N)); setDT(dt)
cat(tracemem(dt), "\n")
cat(tracemem(N), "\n")
cat(tracemem(i), "\n")
while(N > 0L){
set(dt, i, "x", rexp(N, 3) + rexp(N, 3))
set(dt, i, "todo", runif(N, -1, 1) < cos(3.2*pi*dt[i]$x))
N <- length(i <- which(dt$todo))
}
到运行大约需要3秒,时间有点长。我认为还有进一步改进的空间。
system.time({
N <- 1e7; i <- 1:N
dt <- list(x = double(N), todo = logical(N)); setDT(dt)
while(N > 0L){
set(dt, i, "x", rexp(N, 3) + rexp(N, 3))
set(dt, i, "todo", runif(N, -1, 1) < cos(3.2*pi*dt[i]$x))
N <- length(i <- which(dt$todo))
}
})
结果
user system elapsed
3.26 0.10 3.34
我正在对 R 进行拒绝抽样,需要尽可能高效。这是我的原始代码:
N <- 1e8
x <- rexp(N, 3) + rexp(N, 3)
todo <- runif(N, -1, 1) < cos(3.2*pi*x)
while(any(todo)){
x[todo] <- rexp(sum(todo), 3) + rexp(sum(todo), 3)
todo[todo] <- runif(sum(todo), -1, 1) < cos(3.2*pi*x[todo])
}
我在 https://adv-r.hadley.nz/names-values.html 上阅读有关复制和修改的内容,并决定使用 lobstr 包来查找是否有任何相关对象(使用 tracemem()
函数),并且 lo-看哪,逻辑向量 todo
在 while 循环中不断被复制!
如果我做以下测试:
library(lobstr)
N <- 1e8
x <- rexp(N, 3) + rexp(N, 3)
todo <- runif(N, -1, 1) < cos(3.2*pi*x)
cat(tracemem(todo), "\n")
while(any(todo)){
x[todo] <- rexp(sum(todo), 3) + rexp(sum(todo), 3)
todo[todo] <- runif(sum(todo), -1, 1) < cos(3.2*pi*x[todo])
}
我得到以下结果(证实了我的担忧):
[1] "0x38f4938"
[1] "0x1959ff30"
[1] "0x38f4938"
[1] "0x173bb6f0"
[1] "0x38f4938"
[1] "0x4caf788"
[1] "0x38f4938"
[1] "0x1a801628"
[1] "0x38f4938"
[1] "0x18f36768"
[1] "0x38f4938"
[1] "0x4e4d478"
[1] "0x38f4938"
[1] "0x195b93d8"
[1] "0x38f4938"
[1] "0x3f59fe0"
[1] "0x38f4938"
[1] "0x45ebf40"
[1] "0x38f4938"
[1] "0x1a42bdd8"
[1] "0x38f4938"
[1] "0x16c72ba0"
有人可以帮我摆脱这种耗时的行为吗?我尝试了以下声明,但没有成功:
todo <- logical(N)
todo <- list(logical(N))
编辑:另外,如果能帮助我提高代码中这个瓶颈的时间效率,我们将不胜感激...
我认为 base R 不可能做到这一点,但您可以使用 data.table
包优化此过程。试试这个,你会看到没有复制(我还做了一些其他的小改动来进一步优化你的代码)
library(data.table)
N <- 1e7; i <- 1:N
dt <- list(x = double(N), todo = logical(N)); setDT(dt)
cat(tracemem(dt), "\n")
cat(tracemem(N), "\n")
cat(tracemem(i), "\n")
while(N > 0L){
set(dt, i, "x", rexp(N, 3) + rexp(N, 3))
set(dt, i, "todo", runif(N, -1, 1) < cos(3.2*pi*dt[i]$x))
N <- length(i <- which(dt$todo))
}
到运行大约需要3秒,时间有点长。我认为还有进一步改进的空间。
system.time({
N <- 1e7; i <- 1:N
dt <- list(x = double(N), todo = logical(N)); setDT(dt)
while(N > 0L){
set(dt, i, "x", rexp(N, 3) + rexp(N, 3))
set(dt, i, "todo", runif(N, -1, 1) < cos(3.2*pi*dt[i]$x))
N <- length(i <- which(dt$todo))
}
})
结果
user system elapsed
3.26 0.10 3.34