1. 程式人生 > >R中兩種常用並行方法——1. parallel

R中兩種常用並行方法——1. parallel

由於最近在進行一些論文的模擬,所以嘗試了兩種並行的方法:parallelsnowfall,這兩種方法各有優缺,但還是推薦snowfall,整體較為穩定,不容易因為記憶體不足或者並行執行緒過多等原因而報錯。

平行計算

平行計算: 簡單來講,就是同時使用多個計算資源來解決一個計算問題,是提高計算機系統計算速度和處理能力的一種有效手段。(參考:平行計算簡介

  • 一個問題被分解成為一系列可以併發執行的離散部分;
  • 每個部分可以進一步被分解成為一系列離散指令;
  • 來自每個部分的指令可以在不同的處理器上被同時執行;
  • 需要一個總體的控制/協作機制來負責對不同部分的執行情況進行排程。

而在我們平時的模擬中,在一臺電腦或者伺服器上,就是將我們的計算任務分散到多個不同的小的核中同時進行處理。

在模擬時什麼地方可以用到並行?

並行操作一般適用於重複的操作,比如重複隨機按照相同分佈生成資料,然後分別同時進行模擬。這裡就可以用並行。亦或者我們要做permutation計算p-value等資訊,也可以進行並行,因為這種操作是簡單的重複即可完成。

但諸如迭代,遞迴等演算法就很難用並行實現,這種都叫序列。因為後一個的物件需要前一個物件的資訊,只能先算完前一個,再計算後一個內容。

在進行實際的模擬比較多種方法的優劣時,通常需要重複實驗成百上千次,一般可對這裡進行並行操作,寫在這裡的操作是最簡單的。但會有個缺點:可能會出現掛伺服器跑了半天還沒出現結果,但是自己又並不知道執行到哪了的現象。雖然有一些方法可以進行檢視(例如snowfall

中的sfCat()函式,但是輸出的結果是相對來說比較凌亂的,而且有時還會輸出不了,具體用法後面會進行介紹),但是還是可能等很久才出一些結果,如果並行某一個地方維度或者程式碼有些小瑕疵,整段結果都沒法進行輸出。

所以建議,如果能將並行寫到每個演算法中間的話,就儘量寫到每個具體演算法之中(如需要permutation的寫到permutation中;如要多次for迴圈計算統計量以及其它資訊的,直接替代for迴圈),這樣後面實際操作時也比較方便。(這樣做的缺點是可能導致記憶體佔用過多,從而使並行出錯)

怎麼在R中看我們可以使用並行?

只需使用如下命令,就可以檢視我們電腦能夠使用的執行緒數:

detectCores()

理論上這個值 ≥2,我們電腦就可以進行並行操作(現在的電腦基本都是4往上的)。當然通常我們不會使用所有的執行緒來進行並行,不然。。。電腦很可能會崩。

言歸正傳,下面介紹兩種R中常用的並行操作(預設會apply族相關操作)。

parallel(簡單)

一個是parallel包,此包最大的優勢就是非常的便捷,只需將我們原本的apply()修改為parApply()lapply()修改為parLapply()sapply()修改為我們常用的parSapply()等等,然後再在開頭和結尾新增上相應的開始並行與結束並行的語句即可。

首先我們使用lapply()進行下述操作向量化操作:

lapply(1:3, function(x) c(x, x ^ 2, x ^ 3))

輸出結果為:

[[1]]
[1] 1 1 1

[[2]]
[1] 2 4 8

[[3]]
[1]  3  9 27

我們將其修改為並行方法,首先是初始化我們的並行:

library(parallel) # 載入parallel包

# 計算可用執行緒數,並設定並行使用執行緒數
no_cores <- detectCores() - 1

# 初始化
cl <- makeCluster(no_cores)

然後修改原本我們lapply()的命令:

parLapply(cl, 1:3, function(x) c(x, x ^ 2, x ^ 3))

注意:這裡與一般的lapply()相比,要加上cl

輸出結果為:

[[1]]
[1] 1 1 1

[[2]]
[1] 2 4 8

[[3]]
[1]  3  9 27

到這裡我們還沒完,前面初始化我們的並行,這裡需要結束我們的並行,釋放我們用到的執行緒與記憶體,返還給系統。具體使用如下語句:

stopCluster(cl)

至此,一個簡單的並行就完成了。

但事情遠遠沒這麼簡單,在我們需要處理非常複雜的並行任務,反覆使用parallel庫中的並行方法時,我們沒辦法將我們的執行緒數開到最大,有時候甚至連一半都不行,它會出現下面所示的報錯:Error in unserialize(node$con) : error reading from connection,這種情況出現的原因就很複雜了,這是因為“呼叫核心數–計算機記憶體”的不匹配造成的。如果你的資料集很大,呼叫了很多核心,那麼你的計算機記憶體如果不夠匹配,就會出現連線不上的不錯,甚至還出現卡機等現象。簡言之就是爆記憶體了。

解決方法(不能說完全解決,只能說能有效緩解):

  • 使用更少的執行緒進行並行;
  • 如果你的電腦記憶體非常小,有一個簡單的方法確定你的最大使用執行緒:max cores = memory.limit() / memory.size()
  • 將大量的並行分小部分進行;
  • 在程式碼中多使用rm()刪除沒用的變數,使用gc()回收記憶體空間;

不過後面我們介紹另一種並行方式snowfall相對而言就更加穩定了(雖然程式碼寫起來會相對複雜一些),這個我們留待下一篇部落格:R中兩種常用並行方法——2. snowfall 中進行詳細介紹。