是什么导致 `parallel::parSapply` 中的 `external pointer is not valid` 错误?

问题描述

我试图将一个对象(即 R6 类;this particular one)传递给使用 parallel::makePSOCKcluster() 创建的多个工作人员,我得到:

Error in checkForRemoteErrors(val) : 
  one node produced an error: external pointer is not valid

基于this postHenrik Bengtsson

[...] 有一组对象类型不能传递给另一个 R 进程,并且预期在那里工作。

我想了解我尝试传递的对象是否属于此类别,如果属于,我的选择是什么。

这是一个 MRE:

场景 1: (工作) 在每个 worker 内部创建 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 对象并将其传递给工作人员。

(function() {
    # Create cluster.
    cluster <- parallel::makePSOCKcluster(parallel::detectCores() - 1)

    # Stop cluster.
    on.exit(parallel::stopCluster(cluster))

    # Bare minimum data.
    x <- matrix(rnorm(100),10)
    y <- runif(10)

    # The 'osqp' object.
    model <- osqp::osqp(P = crossprod(x),pars = list(verbose = FALSE))

    # Run operation.
    result <- parallel::parSapply(cluster,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,function(i) {
        # Calling the solver.
        return(model$Solve()$x)
    })

    # Inspect result.
    print(result)
})()

解决方法

Roland 指出 environment(model$Solve) 包含一个 private 环境,其中包含一个 externalptr 对象 .work

typeof(model$.__enclos_env__$private$.work)  ​
# "externalptr"

这个指针 .work 是使用编译代码创建的,即通​​过 Rcpp 导出(参见 this export)。

这个指针似乎是由编译代码管理的,因此,我不能在工作人员中使用它。调用编译后的代码并从工作人员内部创建这个指针是可以的。不好的是在另一个进程(即主进程)中创建这个指针,然后将它传递给工作进程。这可能是因为每个 worker 都是作为单独 R 进程创建的,具有自己的内存空间。

不理想,但正如 Roland 指出的那样,可能有效的是以某种方式复制该指针处的数据并确保将其传递给工作人员。但这可能需要 Rcpp 实现。

对于对这个特定包感兴趣的人,您也可以在 GitHub 上关注 this issue

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...