如果减少R中非循环funciton的运行时间?

理由
举报 取消

最近帮老师做事, 因为数据量比较大10000*10000, 要用network,然后做flow betweenness.这样的数据, 跑flowbet function 大概要用6个小时.在下曾经用过doParallel包, 八核一起用可以减少很多运行时间, 但是这个包只能用在循环里.请问大家知道有什么方法可以减少普通function的运行时间吗?谢谢!

2017年6月23日 4 条回复 1107 次浏览

发起人:Leslie Yan 初入职场

Entry-level Data Scientist

回复 ( 4 )

  1. 任坤
    理由
    举报 取消

    可以尝试下面的方法:

    1. 通过Rprof找到速度瓶颈,在瓶颈处优化最有效。

    2. 循环采用多进程并行化不行,可以尝试手动分割任务成可并行的部分,然后手工多进程并行化

    3. 效率低的代码采用高性能的写法(向量化计算、矩阵计算等等)

    4. 如果是矩阵计算有瓶颈,可以用采用了Intel MKL 的 RRO

    4. 用Rcpp重写低效代码

    5. 在Rcpp中用OpenMP等技术实现C++原生数据结构上的多线程并行化。

  2. 黄耀鹏
    理由
    举报 取消

    或许你想要的是Rcpp?

    有一本书可以参考:Seamless.R.and.C++.Integration.with.Rcpp。我没看过,不知道是否有帮助。

  3. 杨益烦
    理由
    举报 取消

    总的来说题主说了个很大的问题。因为R的普通函数本身可能就有很多高科技,只是大家不会用。

    1. apply/for/do.call/…
    apply就是个隐藏的for,但是是vectorized后的;而且同样有mcapply的版本。我要说的是,使用这两个东西,本质没啥区别,连并行版本都是你有我就有这种状态。但是,做这些事情之前想想有没有矩阵内外积(*,%*%,kroneker)表示吧,比如我在这个[帖子](主题:给各位拜年了,小弟又有问题,请教能否不用循环实现?求优化的R代码。)里写的那种矩阵技巧。

    2. data.matrix/data.frame
    data.frame是个很有用的结构,但是在处理海量数据上比matrix差得不是一点半点。matrix本质是按列存储的一个vector;data frame本质和list没有区别。然后你懂得a. 存储需求 data frame大得多 b. 计算速度 data frame 慢得多。但是为什么诸如lm glm使用data fame作为默认输入?因为在数据规模很小的时候你根本看不出来。

    3. R + C/Cpp/F
    好处就不多说了–大家都说了很多–我个人情绪化抵制Rcpp,因为它隐藏了太多东西,而且高度模板化的东西在科学计算领域写起来和调试起来就是脱了裤子放屁。R本身很多函数都是基于编译语言实现的,仔细研究下代码改进成你要的版本是可以的。然后,重要的一点在于…如果算法很糟糕,C的版本可以比R的版本慢你信么,哪怕是你感觉非用for不可时候。你可以看我这个[例子](COS[155412] and some discussion) 理解R数据存储的特点设计算法很重要。

    4. 有关foreach doparallel之类的东西
    其实并行计算的坑是很大的,比如你考虑过你的计算是可重入的不(并行的时候还要做随机数产生)?或者真的可以并行么(EM)?何况有些时候有些隐藏的bug会让人抓狂(比如改了多线程openblas等会在某些平台会让foreach什么的效率降低:都在一个核心上做运算)。

    5. OMP
    你要是自己写R+Cpp(OPENMP)就应该知道,这东西平台依赖…而且,重申一点,不是什么都能并行的,而且怎么并行是个天坑。

    5.1 R本身就不是thread-safe/re-entrant, 也就是原生基于R的API的基本没戏;

    5.2 用GCC + Linux系列的可以自己写OMP+Rcpp可以有解决方案,看Glibc的实现可以看到

    long int
    __random (void)
    {
      int32_t retval;
    
      __libc_lock_lock (lock);
    
      (void) __random_r (&unsafe_state, &retval);
    
      __libc_lock_unlock (lock);
    
      return retval;
    }
    
  4. 王冠嵩
    理由
    举报 取消

    首先,要判断一下瓶颈是否是在function还是在循环。可以用system.time(…)测试运行一次function的事件,和运行比如1000次循环该function的事件。

    如果确实是function的表现不佳,那就想办法优化吧。我能想到的:

    • 是否还有循环可以向量化:尽量避免循环;如果可能用apply系代替循环。
    • 是否可以改进算法:多数回答是不能。
    • 是否可以避免大变量进入和返回function:R会自己判断是否复制进入funciton的变量,如果可能尽量避免传递太大的变量,以免R觉得需要复制一份。
    • 考虑Rcpp:个人认同 @杨益烦。如果你能够基本看出哪段程序影响了速度,那么Rcpp可能对你有用。不然你花很多时间学习、调试的Cpp程序,也许并不比R的实现更高明。

    我也做过(对于我的破笔记本来说)海量的数据,以下是我的建议:

    • 有没有运算服务器可以用?如果是在学校里的话,大多有(相对)更强大的服务器。而且服务器有任务管理,你可以把任务手动分拆然后让服务器同时运行很多R进程(不要太贪心啊)。附加好处,你的电脑也可以空出来打游戏看电视剧用了,还心安理得“我在等服务器运行结果”。
    • 如果你在做模拟,可以考虑选好种子(seed)生成只一次随机数,保存起来,每次运行都用该组随机数。
    • 6小时对于要运行5次左右的话,并不是不能接受。学习Rcpp(和C),调试Rcpp,重写算法,消耗的时间和精力可能会更多。对于研究者,除非真的需要,不要太痴迷于工具。
    • 考虑把function的内容直接写在循环里。一是避免了调用function花费的时间,不过其实这个时间基本上是很小很小的;二是减少了变量在进入function时被复制的可能。
    • 对于循环结果的保存(惨烈的教训)。首先,估计大家都知道的,要事先建立好空的data frame、list或者向量,而不要随着循环改变它们的长度。另外,对长向量/list某一个内容的改变是很费时的,我的解决方法是对于比较大的循环,比如100万次,把循环结果先保存在一个小的向量/list中,比如长度为1万,每1万次循环将其填入到相应的位置。

我来回答

Captcha 点击图片更换验证码