R - 由于无意义的序列化,函数内的并行化性能很差,如何改进? [英] R - terrible parallelisation performance within a function due to pointless serialization, how to improve?

查看:40
本文介绍了R - 由于无意义的序列化,函数内的并行化性能很差,如何改进?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一旦函数环境中有一些东西,序列化所有这些东西(即使不需要)会给并行化增加很大的开销.那么有没有一种在函数内使用并行化的有效方法?我已经尝试过未来的库,但我需要持久的工作人员,如果可行,我宁愿坚持使用基础 R.示例:

test<-function(){clct=parallel::makeCluster(4)a=系统时间()并行::集群调用(clct,函数(x)1)打印(系统时间()-a)大 <- 矩阵(rnorm(8000000))a=系统时间()并行::集群调用(clct,函数(x)1)打印(系统时间()-a)并行::停止集群(clct)}测试()

<块引用>

0.0009980202秒的时间差

0.8078392 秒的时间差

如果我只是将调用集群的行放在全局环境中它们自己的函数中,这可以正常工作,但是一旦我从测试函数环境中传递任何内容(在本例中为 y=4),它又会再次出现坏了:

f1=function(x,y){a=系统时间()并行::集群调用(x,函数(x)y)打印(系统时间()-a)}test2<-function(){clct=parallel::makeCluster(4)f1(clct,4)大 <- 矩阵(rnorm(8000000))f1(clct,4)并行::停止集群(clct)}测试2()

解决方案

你的问题是你创建了函数function(x) 1,其中环境是test<的评估框架/code> 调用.如果你修改它的环境(例如将它设置为与 test 相同,通常是全局环境),它会快得多.例如,在我的系统上,您的原始版本给出了计时

0.003121853秒的时间差时差 1.489178 秒

虽然这个非常相似的版本(在这种情况下相同,但如果函数确实需要访问 test 中的局部变量,则不完全相同):

test<-function(){clct=parallel::makeCluster(4)a=系统时间()并行::集群调用(clct,函数(x)1)打印(系统时间()-a)大 <- 矩阵(rnorm(8000000))f <- 函数(x) 1环境(f) <- 环境(测试)a=系统时间()并行::集群调用(clct,f)打印(系统时间()-a)并行::停止集群(clct)}测试()

产生这些时间:

0.003677845 秒的时间差0.0007309914 秒的时间差

如果你想在 test 中访问一些(但不是全部)局部变量,那会更难,最终会更慢.例如,在这个版本中 f(x) 返回 a 而不是 1:

test<-function(){clct=parallel::makeCluster(4)a=系统时间()并行::集群调用(clct,函数(x)1)打印(系统时间()-a)大 <- 矩阵(rnorm(8000000))a=系统时间()f <- 函数(x) aenv <- new.env(parent = environment(test))env$a <- a环境(f) <- 环境并行::集群调用(clct,f)a <- env$a # 以防万一 f 修改了它打印(系统时间()-a)并行::停止集群(clct)}测试()

我来了

0.003219843秒的时间差0.001209021秒的时间差

Once a function environment has some stuff in it, serializing all this stuff (even when it is not needed) adds a big overhead to parallelization. Is there then an effective way to use parallelization within a function? I've tried the future library but I need persistent workers, and would rather stick with base R if feasible. Example:

test<-function(){
  clct=parallel::makeCluster(4)

  a=Sys.time()
  parallel::clusterCall(clct,function(x) 1)
  print(Sys.time()-a)

  big <- matrix(rnorm(8000000))

  a=Sys.time()
  parallel::clusterCall(clct,function(x) 1)
  print(Sys.time()-a)

  parallel::stopCluster(clct)
}

test()

Time difference of 0.0009980202 secs

Time difference of 0.8078392 secs

If I simply put the lines calling the cluster in their own function in the global environment, this works fine, but then as soon as I pass anything (in this case, y=4) from the test function environment, it's again broken:

f1=function(x,y){
  a=Sys.time()
  parallel::clusterCall(x,function(x) y)
  print(Sys.time()-a)
}

test2<-function(){
  clct=parallel::makeCluster(4)
f1(clct,4)

  big <- matrix(rnorm(8000000))
f1(clct,4)
  parallel::stopCluster(clct)
}

test2()

解决方案

Your problem is that you create the function function(x) 1 with environment being the evaluation frame of the test call. If you modify its environment (e.g. set it the same as test, which would normally be the global environment), it will be much faster. For example, on my system your original version gave timings

Time difference of 0.003121853 secs
Time difference of 1.489178 secs

while this very similar version (identical in this case, but not identical if the function really needed access to local variables in test):

test<-function(){
  clct=parallel::makeCluster(4)

  a=Sys.time()
  parallel::clusterCall(clct,function(x) 1)
  print(Sys.time()-a)

  big <- matrix(rnorm(8000000))
  f <- function(x) 1
  environment(f) <- environment(test)
  a=Sys.time()
  parallel::clusterCall(clct,f)
  print(Sys.time()-a)

  parallel::stopCluster(clct)
}

test()

produces these timings:

Time difference of 0.003677845 secs
Time difference of 0.0007309914 secs

If you had wanted access to some (but not all) local variables in test, it would have been harder, and would end up slower. For instance, in this version f(x) returns a instead of 1:

test<-function(){
  clct=parallel::makeCluster(4)

  a=Sys.time()
  parallel::clusterCall(clct,function(x) 1)
  print(Sys.time()-a)

  big <- matrix(rnorm(8000000))

  a=Sys.time()
  f <- function(x) a
  env <- new.env(parent = environment(test))
  env$a <- a
  environment(f) <- env
  parallel::clusterCall(clct,f)
  a <- env$a  # Just in case f modified it
  print(Sys.time()-a)

  parallel::stopCluster(clct)
}

test()

Here I get

Time difference of 0.003219843 secs
Time difference of 0.001209021 secs

这篇关于R - 由于无意义的序列化,函数内的并行化性能很差,如何改进?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆