我正在开发一个通过管道工 API 调用 R 的应用程序,每个 API 都嵌入在未来的承诺中(使用多会话计划)。 API 的结构是这样的:
#* API1
#* @get species/<scientific_name>/API1
#* @param scientific_name:string Scientific Name
#* @serializer unboxedJSON
#* @tag test
function(scientific_name) {
Prom<-future({
result <- CODE_OF_THE_PROMISE
gc()
return(list(object_to_return=result))
}, gc=T, seed=T)
gc()
return(Prom)
}
尽管在返回结果和承诺之前放置了 gc(),我还是看到了一些 RAM 积累(每个 API 调用几十或几百 MB)。有时我目睹这部分记忆后来被释放,但并非总是如此。我希望 API 结果返回后,所有对象都会被删除,然后所有 RAM 都会被释放。您是否看到任何明显的原因导致情况并非如此以及可以采取什么措施?
我在下面放置了一个可重现的示例。这将是主要代码:
### Set the asynchronous coding
library(promises) ; library(future)
future::plan("multisession")
### Plumber app
library(plumber)
### Other libraries
library(terra)
### Start app
pr <- pr("API.R")
pr %>% pr_run()
这将是存储在文件 API.R 中的代码:
#* API1
#* @get species/<scientific_name>/API1
#* @param scientific_name:string Scientific Name
#* @serializer unboxedJSON
#* @tag test
function(scientific_name) {
Prom<-future({
raster_test<-rast(xmin=-180, xmax=180, ymin=-90, ymax=90, resolution=0.1, crs="+init=epsg:4326", vals=rnorm(259200, 1, 10))
result <- sum(as.vector(raster_test))
gc()
return(list(object_to_return=result))
}, gc=T, seed=T)
gc()
return(Prom)
}
当我使用任何科学名称(没关系)运行 API1 时,我的 R 会话在执行 API 之前需要 850MB,之后需要 930MB。接下来的几分钟内存没有被释放。
如果需要,这是我的会话信息:
R version 4.2.3 (2023-03-15 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 22621)
Matrix products: default
locale:
[1] LC_COLLATE=French_France.utf8 LC_CTYPE=French_France.utf8 LC_MONETARY=French_France.utf8 LC_NUMERIC=C LC_TIME=French_France.utf8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] terra_1.7-3 plumber_1.2.1 future_1.32.0 promises_1.2.0.1
loaded via a namespace (and not attached):
[1] Rcpp_1.0.10 codetools_0.2-19 listenv_0.9.0 digest_0.6.31 later_1.3.0 parallelly_1.34.0 R6_2.5.1 lifecycle_1.0.3
[9] jsonlite_1.8.4 magrittr_2.0.3 stringi_1.7.12 rlang_1.1.0 cli_3.6.0 swagger_3.33.1 rstudioapi_0.14 ellipsis_0.3.2
[17] webutils_1.1 tools_4.2.3 httpuv_1.6.8 parallel_4.2.3 compiler_4.2.3 globals_0.16.2
感谢您的帮助!
(这里是Futureverse的作者)
垃圾收集总是很棘手 - 不仅在 R 中。很难预测垃圾收集器在 R 中运行的确切时间。使用
future(..., gc = TRUE)
是这里的正确尝试。使用 plan(multisession)
时,未来框架将在收集结果并且从工作线程中擦除所有变量(全部自动完成)后立即在并行工作线程上调用 gc()
。但正如您所注意到的,这并不总是足够好。
multisession
后端依赖于持久PSOCK集群工作人员。因为它们是持久性的,所以它们会一直存在直到关闭,并且无论是否正在处理任务,它们都将始终消耗内存。根据操作系统的不同,它们在将取消分配的内存返回给操作系统方面也可能或多或少有效。
话虽如此,您可以尝试
future.callr::callr
后端。它依赖于transient并行工作者。每个 future 都会在一个动态启动的新后台工作程序中进行评估,一旦收集到结果,该工作程序就会再次关闭。这样就避免了内存消耗随时间累积的问题。