2012-08-31 54 views
3

過帳Best way to allocate matrix in R, NULL vs NA?表明在R中編寫自己的矩陣分配函數可能比使用R的內置矩陣()函數預先分配大矩陣快8到10倍。R中矩陣分配更快的原因

有誰知道爲什麼手工製作的功能是如此之快? R在矩陣()內做什麼很慢?謝謝。

這裏是我的系統上的代碼:

create.matrix <- function(nrow, ncol) { 
x<-matrix() 
length(x) <- nrow*ncol 
dim(x) <- c(nrow,ncol) 
x 
} 

system.time(x <- matrix(nrow=10000, ncol=9999)) 
user system elapsed 
1.989 0.136 2.127 

system.time(y <- create.matrix(10000, 9999)) 
user system elapsed 
0.192 0.141 0.332 
identical(x,y) 
[1] TRUE 

我appologize那些誰評論認爲用戶定義的函數是慢的,因爲什麼被張貼在回答在上面的鏈接是不一致的。我正在查看用戶時間,在上述鏈接中速度提高了8倍,在我的系統上,用戶定義內置速度提高了10倍。

在迴應約書亞的會話信息請求:

> sessionInfo() 
R version 2.12.1 (2010-12-16) 
Platform: x86_64-unknown-linux-gnu (64-bit) 

locale: 
[1] LC_CTYPE=en_US.UTF-8  LC_NUMERIC=C    
[3] LC_TIME=en_US.UTF-8  LC_COLLATE=en_US.UTF-8  
[5] LC_MONETARY=C    LC_MESSAGES=en_US.UTF-8 
[7] LC_PAPER=en_US.UTF-8  LC_NAME=C     
[9] LC_ADDRESS=C    LC_TELEPHONE=C    
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C  

attached base packages: 
[1] stats  graphics grDevices utils  datasets methods base  

loaded via a namespace (and not attached): 
[1] tools_2.12.1 

而且,我試圖運行西蒙的三個例子,第三個例子西蒙給出的最快的,原來對我來說最慢的:

> system.time(matrix(NA, nrow=10000, ncol=9999)) 
    user system elapsed 
    2.011 0.159 2.171 
> system.time({x=NA; length(x)=99990000; dim(x)=c(10000,9999); x}) 
    user system elapsed 
    0.194 0.137 0.330 
> system.time(matrix(logical(0), nrow=10000, ncol=9999)) 
    user system elapsed 
    4.180 0.200 4.385 

但是我仍然認爲西蒙可能是在正確的軌道上,想到matrix()最初分配1x1矩陣然後複製它。任何人都知道R內部的任何良好的文檔?謝謝。

+1

我不確定我關注。這個問題中的自定義函數_slower_是不是(即使這樣,大約是3)?正如在評論中指出的那樣,使用矢量化總是會快得多。 – joran

+2

在接受的答案中,我看到用戶定義的函數比僅使用「矩陣」慢3倍。你能否提供比'矩陣'快8-10倍的自定義函數的例子? –

+1

另請注意,在鏈接中,2個對象有不同的存儲模式(向量(可能是邏輯)與列表),所以雖然它們都是基礎,但它們在存儲的內容和附帶的開銷之間差別很大。 –

回答

8

的問題是,您的通話matrix是有點比你想象的更復雜。比較以下版本:

# copy NA matrix 
> system.time(matrix(NA, nrow=10000, ncol=9999)) 
    user system elapsed 
    1.272 0.224 1.496 

# replicate NA vector (faster version of what you used) 
> system.time({x=NA; length(x)=99990000; dim(x)=c(10000,9999); x}) 
    user system elapsed 
    0.292 0.260 0.552 

# fastest - just allocate a matrix filled with NAs 
> system.time(matrix(logical(0), nrow=10000, ncol=9999)) 
    user system elapsed 
    0.184 0.308 0.495 

所以在你的榜樣,你基本上是建立其複製到你指定的大小1×1 NA矩陣 - 最慢的方法。做同樣與向量更快(因爲它不需要使用列模) - 你這麼做是有點複雜的方式(通過創建一個矩陣,將其轉換爲載體,然後再返回到一個矩陣),但這個想法是一樣的。最後,如果你只是使用一個空矢量,那麼矩陣將會簡單地填滿你想要的東西,因此不需要額外的工作(最快)。

編輯一個重要的注意,儘管馬修的建議是正確的,雖然它並沒有參與(因爲他引用的代碼是logical(0)情況下,而不是在NA情況下)。無意中我在上述時間中運行R-devel,因此發佈的R中的時間會有所不同。

+0

感謝您快速更改do_matrix。但是我認爲你在上面的第三種情況下使用的是固定版本,因爲傳遞給'matrix()'的數據長度爲0,因此在C do_matrix中使用'lendat' 0,然後運行新的更高效的代碼。要檢查,在R 2.15.1中運行你的計時錶明第一次和第三次同時運行,對我來說。 –

+0

或換句話說,我同意我的建議沒有涉及,但現在通過設置'data = logical(0)':-) –

+0

好了。現在我們正在某個地方。我還是不太明白裏面發生了什麼。聽起來像'matrix(nrow = 10000,ncol = 9999)'與'matrix(data = NA,nrow = 10000,ncol = 9999)'相同,並且都很慢,因爲它們首先創建單個元素矩陣,然後將其複製(10000 * 9999)次。 'matrix(邏輯(0),nrow = 10000,ncol = 9999)'與另外兩個不同? 「邏輯(0)」表示NA不是?謝謝。 –

5

雖然我理解他們中的大多數,但我會對評論提出問題。問題在於,引用的帖子有一個答案,其內部矛盾是評論者未經檢查而依賴的。用戶和系統的時間不適當地加在一起,因爲他們應該這樣做。

create.matrix <- function(size) { 
    x <- matrix() 
    length(x) <- size^2 
    dim(x) <- c(size,size) 
    x 
    } 
    system.time(x <- matrix(data=NA,nrow=10000,ncol=10000)) 
# user system elapsed 
# 0.464 0.226 0.688 
system.time(y <- create.matrix(size=10000)) 
# user system elapsed 
# 0.177 0.239 0.414 

我懷疑的效率實際上是由事實來實現用戶自定義功能只能夠創建一個方陣和「矩陣」的需要爲一個更一般的情況下做的論證有效性檢查。

編輯:我看到你有一個我的假設(關於矩陣矩陣限制)被證明是錯誤的,我也會注意到,我的另一個假設是,這是以某種方式由於懶惰評估也失敗了我的測試。差異確實沒有意義,因爲用戶代碼使用matrix函數。

+0

我也對註釋加以排除,特別是他們認爲矩陣比用戶定義的函數更快的地方。然而,我很抱歉沒有更仔細地看待發布的時間(我正在談論的是合適的答案,而不是發佈本身)。你是正確的,他們不加起來。這是它在我的系統上的外觀,以及一個非方形矩陣:(下一條評論)。 –

+0

「create.matrix < - 函數(nrow,NcoI位){ X < - 矩陣() 長度(X)< - nrow * NcoI位 暗淡(X)< - C(nrow,NcoI位) X } > system.time(x < - matrix(nrow = 10000,ncol = 9999))用戶系統已用1.989 0.136 2.127 > system.time(y < - create.matrix(10000,9999))用戶系統已用0.192 0.141 0.332 >相同(x,y)[1] TRUE' –

+0

@ user1639359:如果您將這些示例添加到您的問題中(如我在我的評論中所要求的那樣)會更好。 –

1

嗯。是的,這很奇怪。 ...這是略快尚 - 儘管它更像矩陣(),它允許一個單一的數據參數(但必須是標量):

create.matrix2 <- function(data=NA, nrow, ncol) { 
    x <- rep.int(data[[1]], nrow*ncol) 
    dim(x) <- c(nrow, ncol) 
    x 
} 
system.time(x <- matrix(nrow=10000, ncol=9999)) # 0.387 secs 
system.time(y <- create.matrix(nrow=10000, ncol=9999)) # 0.199 secs 
system.time(z <- create.matrix2(nrow=10000, ncol=9999)) # 0.173 secs 
identical(x,z) # TRUE 

...我猜的內部代碼創建矩陣做了浪費的(也可能是有用的,但我不能想到什麼,這將是)......

哦,因爲它處理任何長度的data,它可能最終類似rep(data, length.out=nrow*ncol)東西這是相當慢的:

system.time(rep(NA, length.out=10000*9999)) # 1.5 secs! 

無論如何,絕對有改進的餘地!

4

不知道這是什麼原因(可能是不同的低效率),但在src/array中的do_matrix。c有一個類型開關包含:

case LGLSXP : 
    for (i = 0; i < nr; i++) 
    for (j = 0; j < nc; j++) 
     LOGICAL(ans)[i + j * NR] = NA_LOGICAL; 

這看起來是頁面效率低下。認爲應該是:

case LGLSXP : 
    for (j = 0; j < nc; j++) 
    for (i = 0; i < nr; i++) 
     LOGICAL(ans)[i + j * NR] = NA_LOGICAL; 

或者更簡單地說:

case LGLSXP : 
    for (i = 0; i < nc*nr; i++) 
     LOGICAL(ans)[i] = NA_LOGICAL; 

(以來NR需要進行一些微調是類型R_xlen_tincnrint型)。


UPDATE:

張貼到R-devel的經過:

Possible page inefficiency in do_matrix in array.c

西蒙Urbanek現在已經犯了變化R.它現在使用上述單項指標的方法:

​​

但是,正如西蒙所說,我在上面覆蓋自己,似乎並沒有解決問題提出的特定問題。另一個不同的低效率也需要找到並修復。


這可能是後續解決的問題。這結合了新代碼的頁面效率(現在在R中),但是,當matrix(data=NA)(R的默認值)時使用該代碼。這避免了在copyMatrix模西蒙提到了他的答案,避免copyMatrixNA情況。

目前,do_matrix在array.c有:

if(lendat) { 
    if (isVector(vals)) 
     copyMatrix(ans, vals, byrow); 
    else 
     copyListMatrix(ans, vals, byrow); 
} else if (isVector(vals)) { 
    // fill with NAs in the new page efficient way that Simon already committed. 
} 

這可能是如下。我不知道在C水平是需要SEXP輸入一個ISNA()功能,於是紛紛編碼的長手(西蒙,有沒有更好的辦法?):

if(lendat && // but not a single NA, basically : 
      !(lendat==1 && 
        ((isLogical(vals) && LOGICAL(vals)[0] == NA_LOGICAL) || 
        (isReal(vals) && ISNA(REAL(vals)[0])) || 
        (isInteger(vals) && INTEGER(vals)[0] == NA_INTEGER)))) { 
    if (isVector(vals)) 
     copyMatrix(ans, vals, byrow); 
    else 
     copyListMatrix(ans, vals, byrow); 
} else if (isVector(vals)) { 
    // fill with NAs in the new page efficient way that Simon already committed. 
    // this branch will now run when dat is a single NA, too 
} 
+0

可能是一個失敗的原因,但你有沒有想過發送到r-devel? (或Radford Neal?) –

+0

@Ben Ok,剛剛發佈到r-devel。手指交叉... –