在對(duì)一個(gè)大矩陣執(zhí)行相關(guān)性計(jì)算或Jaccard Index的計(jì)算時(shí),其實(shí)執(zhí)行的是矩陣任意兩行(這里假設(shè)要進(jìn)行分析的對(duì)象是矩陣的每個(gè)行)之間的兩兩的計(jì)算,若這個(gè)矩陣的規(guī)模非常龐大,有n行時(shí),計(jì)算的時(shí)間復(fù)雜度就是,這個(gè)時(shí)候可以采用并行化策略來加速這個(gè)進(jìn)程(參考上文的 2. R中的并行化方法):
StatOut <- parApply(cl, data, 1, fun, data)
這樣就會(huì)實(shí)現(xiàn)將一個(gè) n vs. n 的問題拆分成 n 個(gè)可以并行解決的 1 vs. n 的問題,當(dāng)然通過設(shè)置線程數(shù)為,使得每次只并行執(zhí)行m個(gè)
1 vs. n 的問題
然后再在函數(shù)fun內(nèi)部再定義一個(gè)并行化計(jì)算來進(jìn)一步并行化加速上面產(chǎn)生的 1 vs. n 的計(jì)算:
fun <- function(vec, data){
...
parApply(cl, data, 1, fun2, vec)
}
在這個(gè)函數(shù)內(nèi)部實(shí)現(xiàn)了將一個(gè) 1 vs. n 的問題拆分成 n 個(gè)可以并行解決的 1 vs. 1 的問題
這樣就實(shí)現(xiàn)了兩步并行化,這樣在保證硬件條件滿足的情況下,的確能顯著加快分析速度
但是并行化技術(shù)會(huì)帶來一個(gè)問題是:雖然時(shí)間開銷減少了,但是空間開銷顯著增加了
比如,第一次并行化,將一個(gè)
n vs. n的問題拆分成次可以并行解決的 m個(gè)
1 vs. n的問題,則需要在每一次并行化任務(wù)中拷貝一個(gè)1 vs. n的計(jì)算對(duì)象,即原始有n行的矩陣被拷貝了m次,則相應(yīng)的緩存空間也增加了m倍,很顯然內(nèi)存的占用大大增加了
空間開銷顯著增加帶來的后果就是,很容易導(dǎo)致運(yùn)行內(nèi)存不足程序運(yùn)行中斷的問題,那該怎么解決這個(gè)問題呢?
可以采用分治方法(Divide-Conquer),將原始大矩陣,按照行拆分成多個(gè)小的子塊,對(duì)每個(gè)子塊執(zhí)行計(jì)算,從而得到每個(gè)子塊的運(yùn)算結(jié)果,最后再講每個(gè)子塊的結(jié)果進(jìn)行合并:
n_row <- nrow(data)
nblock <- 10 # 用于設(shè)定子塊的數(shù),用戶可以自行指定
# 該列表變量用于保存每個(gè)子塊的計(jì)算結(jié)果,若總共拆成了m個(gè)子塊,
# 因?yàn)橐M(jìn)行任意兩子塊的計(jì)算,因此會(huì)有mxm個(gè)子塊計(jì)算結(jié)果,因
# 此該列表要保存的數(shù)據(jù)為mxm維度的數(shù)據(jù),每個(gè)元素為一個(gè)計(jì)算結(jié)
# 果,它們都是矩陣
StatOutList <- vector("list", nblock)
# 1. 開始進(jìn)行分塊計(jì)算
print("[Divide-Conquer]Start carry out Divide-Conquer for Large Observed Matrix")
print("##################################################")
for(i in 1:nblock){
for(j in 1:nblock){
nrow_start <- (i-1)*nrow_block+1
nrow_end <- i*nrow_block
# 并行化計(jì)算
if(!is.na(Args[2])){
print(paste("[Divide-Conquer]Start carry out statistic Jaccard Index parallel for block: ",i,"-",j,sep=''))
StatOutList[[i]] <- append(StatOutList[[i]], parApply(cl, data[nrow_start:nrow_end,], 1 , JaccardIndexSer, data))
print(paste("[Divide-Conquer]Finish run parallel for block: ",i,"-",j,sep=''))
# 串行計(jì)算
}else{
print(paste("[Divide-Conquer]Start carry out statistic Jaccard Index serially for block: ",i,"-",j,sep=''))
StatOutList[[i]] <- append(StatOutList[[i]], apply(data, 1 , JaccardIndexSer, data))
print(paste("[Divide-Conquer]Finish run serially for block: ",i,"-",j,sep=''))
}
}
}
# 2. 結(jié)束分治方法的分塊計(jì)算
if(!is.na(Args[2])){
print("##################################################")
print("[Divide-Conquer]Finish parallel running for statistic Jaccard Index!")
stopCluster(cl)
}else{
print("##################################################")
print("[Divide-Conquer]Finish serial running for statistic Jaccard Index!")
}
# 3. 開始進(jìn)行子塊結(jié)果的合并
print("[Divide-Conquer]Start bind sub-block statout")
StatOut <- vector("list", nblock)
# 先對(duì)列進(jìn)行合并
for(i in 1:nblock){
for(block in StatOutList[[i]]){
StatOut[[i]] <- cbind(StatOut[[i]], block)
}
}
# 再對(duì)行進(jìn)行合并
StatOutMerge <- data.frame()
for(block in StatOut){
StatOutMerge <- rbind(StatOutMerge, block)
}