我正试图将一个对象(即R6
类的对象;这个特定的对象(传递给使用parallel::makePSOCKcluster()
创建的许多工作者,我得到:
Error in checkForRemoteErrors(val) :
one node produced an error: external pointer is not valid
基于Henrik Bengtsson的这篇文章:
[…]有一组对象类型不能传递给另一个R进程,并且应该在那里工作。
我想了解我试图传递的对象是否属于这一类,如果是,我的选项是什么。
这是一个MRE:
场景1:(工作(在每个工作程序中创建model
对象。
(function() {
# Create cluster.
cluster <- parallel::makePSOCKcluster(parallel::detectCores() - 1)
# Stop cluster.
on.exit(parallel::stopCluster(cluster))
# Bare minimum data.
x <- matrix(rnorm(100), 10, 10)
y <- runif(10)
# Run operation.
result <- parallel::parSapply(cluster, c(1), function(i) {
# The 'osqp' object.
model <- osqp::osqp(P = crossprod(x), q = -crossprod(x, y), pars = list(verbose = FALSE))
# Calling the solver.
return(model$Solve()$x)
})
# Inspect result.
print(result)
})()
场景2:(不工作(在main中创建model
对象并将其传递给worker。
(function() {
# Create cluster.
cluster <- parallel::makePSOCKcluster(parallel::detectCores() - 1)
# Stop cluster.
on.exit(parallel::stopCluster(cluster))
# Bare minimum data.
x <- matrix(rnorm(100), 10, 10)
y <- runif(10)
# The 'osqp' object.
model <- osqp::osqp(P = crossprod(x), q = -crossprod(x, y), pars = list(verbose = FALSE))
# Run operation.
result <- parallel::parSapply(cluster, c(1), function(i) {
# Calling the solver.
return(model$Solve()$x)
})
# Inspect result.
print(result)
})()
场景1有效,因此我似乎可以在工作人员内部使用osqp
。但是,当我在外部创建该对象并将其传递给工人(即场景2(时,它失败了。
为了提供更多的上下文,我无法控制model
的创建。我正在接收一个在其他地方创建的实例,并且只允许在该实例上调用几个方法(例如$Update()
(。
更新1
这似乎与R6
实例是环境这一事实无关。以下仍然按预期工作。
# Create mock model class.
ModelMock <- R6::R6Class("ModelMock",
public = list(
Solve = function() {
return(list(x = "Mocked model output."))
}
)
)
(function() {
# Create cluster.
cluster <- parallel::makePSOCKcluster(parallel::detectCores() - 1)
# Stop cluster.
on.exit(parallel::stopCluster(cluster))
# The mocked 'osqp' object.
model <- ModelMock$new()
# Run operation.
result <- parallel::parSapply(cluster, c(1), function(i) {
# Calling the solver.
return(model$Solve()$x)
})
# Inspect result.
print(result)
})()
Roland指出environment(model$Solve)
包含一个包含externalptr
对象.work
:的private
环境
typeof(model$.__enclos_env__$private$.work)
# "externalptr"
该指针.work
是使用编译后的代码创建的,即通过Rcpp
导出(请参阅此导出(。
这个指针似乎是由编译的代码管理的,因此,我不能在worker中使用它。调用编译后的代码并在worker中创建此指针是可以的。不好的是在另一个进程(即主进程(中创建此指针,然后将其传递给辅助进程。这可能是因为每个工作进程都创建为一个独立的R
进程,具有自己的内存空间。
这并不理想,但正如罗兰所指出的,可能有效的方法是以某种方式复制该指针上的数据,并确保将其传递给工人。但这可能需要Rcpp
实现。
对于那些对这个特定包感兴趣的人,你也可以在GitHub上关注这个问题。