在 R 中, 利用并行时,老是跑不完完整:100次跑70次之类的,偶尔还会有如下警告,想请问大家为什么?

代码示例

for(ij in 1 : 100)
      {
        t.start = Sys.time()
        cl = makeCluster(getOption("cl.cores", 3))
        envil = environment(lin_ben_model)
        registerDoSNOW(cl)
        iterations = randn
        pb = txtProgressBar(max = iterations, style = 3)
        progress = function(n) setTxtProgressBar(pb, n)
        opts = list(progress = progress)
        Re = foreach(r = r.start : min((r.start + randn - 1), randn), .combine = "rbind", .options.snow = opts,
                 .packages = c("doSNOW", "parallel", "GPfit"),
                 export = ls(envil)) %dopar%
        {
          index.s = index_all[r, ]
          train.x = traindata[index.s, 1 : ncol(traindata) - 1]
          train.y = traindata[pre.i, 2 : ncol(traindata)]
          train.er = LOOtest(train.x, train.y)
          temp = c(index.s, train.er)
          return(temp)
        }
        t.stop = Sys.time()
        print(t.stop - t.start)
        stopCluster(cl)
        Ree = rbind(Ree, Re)
        r.start = nrow(Ree) + 1
        if(r.start > randn){break}
      }

上面的foreach 中跑不完整,所以我外面再加了一次循环。

除此之外,还会有警告或者错误如下:

警告

UseMethod("solve")里有警告:
关闭不再使用的链结5(<-DESKTOP-6PRLU93:11442)
UseMethod("solve")里有警告:
关闭不再使用的链结4(<-DESKTOP-6PRLU93:11442)
UseMethod("solve")里有警告:
关闭不再使用的链结3(<-DESKTOP-6PRLU93:11442)

错误

错误于unserialize(socklist[[n]]): 读取链结时发生了错误

第一点我还是想说至少提供个最小可重复的例子,这样大家才能帮你排查错误。

如果你觉得是foreach并行的问题的话,那么可以试着先使用foreach的单线程后端,而不是snow这种并行后端来测试一下代码是否能正常运行。

这些“警告”一般是系统发现有遗留的子线程,但是对应的主线程已经不在了,所以选择关闭这些子线程。这可能意味着之前的计算有一些并行计算的处理没有做好,但是一般这中警告可以忽略。不过这些提示出现在UseMethod("solve")里,倒是让我猜测是不是除了你这里自己写的并行计算代码外,实际计算中是不是软件包或者函数也是使用了并行的?然后其实出错是在那些计算中而不是你的foreach的并行。

关于那个“错误”,unserialize(socklist[[n]])无法读取的话,一般就是子线程出错挂掉了,于是主线程无法连接上,就报错了。一般这种时候,可以考虑先进行单线程的调试,或者在进行并行计算的时候尽量去保存子线程的输出来辅助定位错误。可以考虑使用foreachfuture后端,或者在makeCluster中指定outfile参数

关于代码本身,作为一个没法跑的代码片段,当中有一些变量、函数肯定是缺上下文定义的。我打眼一瞧能看到的可能的问题是traindata[index.s, 1 : ncol(traindata) - 1]里面,1 : ncol(traindata) - 1其实对应的是0 :(ncol(traindata) - 1),我不确定这是不是真的是你要的指标集。

    fenguoerbian 谢谢你的回复。确实应该提交一个简单的例子,一个最简单的生存均匀分布 $U(0,1)$ 随机数如下:

    library(parallel)
    library(doSNOW)
    t.start = Sys.time()
    f1 = function(x){return(x)}
    cl = makeCluster(getOption("cl.cores", 3))
    registerDoSNOW(cl)
    envil = environment(f1)
    iterations = 1000
    pb = txtProgressBar(max = iterations, style = 3)
    progress = function(n) setTxtProgressBar(pb, n)
    opts = list(progress = progress)
    Re = foreach(r = 1:1000, .combine = "rbind", .options.snow = opts, export = ls(envil)) %dopar% {
      temp = runif(1)
      return(temp)
    }
    
    t.stop = Sys.time()
    print(t.stop - t.start)
    stopCluster(cl)

    结果只能跑出10个结果,结果如下:

    source("C:/Users/chan/Downloads/dotest.R")
      |=                                                                                  |   1%Time difference of 0.528765 secs

    后面检查好像是 export = ls(envil) 漏掉了 . 改成 .export = ls(envil) 没有问题了。就是之前那种也不报错,就是只运行前面一部分。谢谢您

      tjmath

      不报错是因为这本来就是foreach定义好的行为。可以参考一下文档发现,在提供了多个指标变量的时候,会按照其中最短的变量来进行循环。所以对于我们自己而言,可以发现这是参数名称写错了,但是对于foreach而言,那就是用户又提供了一个指标变量叫作export而已。

      所以这个例子里可以正常跑完而不报错,跟你最开始提到的会明确出现unserialize(socklist[[n]])无法读取的错误可能还是不一样?

        fenguoerbian fenguoerbian 哦哦,原来少一个点就是会定义一个新的指标。会出现 unserialize(socklist[[n]]) 的原因我认为是因为我是这样做的

        library(parallel)
        library(doSNOW)
        Ree = NULL
        rstart = 1
        for(ij in 1:100)
        {
          t.start = Sys.time()
          f1 = function(x){return(x)}
          cl = makeCluster(getOption("cl.cores", 3))
          registerDoSNOW(cl)
          envil = environment(f1)
          iterations = 1000
          pb = txtProgressBar(max = iterations, style = 3)
          progress = function(n) setTxtProgressBar(pb, n)
          opts = list(progress = progress)
          Re = foreach(r = rstart:1000, .combine = "rbind", .options.snow = opts, export = ls(envil)) %dopar% {
            temp = runif(1)
            return(temp)
          }
          Ree = rbind(Ree, Re)
          t.stop = Sys.time()
          print(t.stop - t.start)
          stopCluster(cl)
          if(nrow(Ree) == 1000){break}
          rstart = nrow(Ree) + 1
          
        }

        因为之前错误使用 export = ls(envil), 导致每次只能跑一些结果,我就在最外面又套了一个for循环,确保Ree能得到1000次结果。这样频繁可能导致警告或错误