3

R、NULL対NAで行列を割り当てる最良の方法を投稿していますか? は、R で独自の行列割り当て関数を記述すると、R の組み込みの matrix() 関数を使用して大きな行列を事前に割り当てるよりも 8 倍から 10 倍高速になることを示しています。

手作りの関数がなぜそんなに速いのか誰か知っていますか? R は非常に遅い matrix() 内で何をしているのですか? ありがとう。

私のシステムのコードは次のとおりです。

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

上記のリンクの回答に投稿されている内容が一貫していないため、ユーザー定義関数が遅いと考えてコメントした人に謝罪します。上記のリンクでは約 8 倍高速であり、私のシステムでは、ユーザー定義と組み込みの場合で約 10 倍高速です。

Joshua のセッション情報のリクエストに応じて:

> 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

また、Simon の 3 つの例を実行してみましたが、Simon が最速として挙げた 3 番目の例は、私にとっては最も遅いことがわかりました。

> 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 の内部に関する優れたドキュメントを知っている人はいますか? ありがとう。

4

4 に答える 4

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 x 1 NA マトリックスを作成していました。これは最も遅いアプローチです。ベクトルで同じことを行う方が高速です(列にモジュロを使用する必要がないため)-少し複雑な方法で行いました(行列を作成し、それをベクトルに変換してから行列に戻す)が、考え方は同じです。最後に、空のベクトルを使用するだけの場合、NAとにかく必要なものが行列に s で埋められるため、余分な作業は必要ありません (最速)。

EDITただし、重要な注意:マシューの提案は、関与していませんでしたが正しかったです(彼が引用したコードはlogical(0)ケースではなくケースであるためNA)。うっかり上記のタイミングで R-devel を実行していたため、リリースされた R のタイミングは異なります。

于 2012-09-03T03:06:52.683 に答える
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 

ユーザー定義関数は正方行列しか作成できず、「行列」はより一般的な状況で引数の有効性をチェックする必要があるという事実によって、効率が実際に達成されているのではないかと思います。

編集: 私の仮説の 1 つ (正方行列の制限に関して) が反証されたことがわかりました。また、これが何らかの形で遅延評価によるものであるという私の他の仮説も、私のテストに失敗したことに注意してください。ユーザーコードが関数を使用しているため、不一致は実際には意味がありませんmatrix

于 2012-08-31T19:00:14.787 に答える
4

これが原因であるかどうかはわかりませんが (別の非効率性である可能性があります)、do_matrixsrc/array.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( is type R_xlen_twhile i, ncand nrare typeであるため、微調整が必​​要ですint)。


アップデート:

r-devel に投稿した後:

array.c の do_matrix でページの非効率性が考えられる

Simon Urbanek が R に変更をコミットしました。現在は、上記の単一インデックス アプローチを使用しています。

array.c の最新のライブ バージョン

しかし、サイモンが言うように、私は上記で自分自身をカバーしましたが、これは質問によって提起された特定の問題を解決するようには見えません. 2 つ目の別の非効率性も見つけて修正する必要があります。


これはおそらく、そのフォローアップ修正が何であるかです. matrix(data=NA)これは、新しいコード (現在は R) のページ効率を組み合わせますが、 (R のデフォルト)場合はそれを使用するように切り替えます。これは、ケースcopyMatrixを回避することにより、サイモンが彼の答えで言及しているモジュロを回避します。copyMatrixNA

現在、array.c の do_matrix には次のものがあります。

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.
}

これは次のようになります。ISNA()入力を受け取るC レベルの関数を認識していないSEXPので、長い手でコーディングしました (サイモン、もっと良い方法はありますか?):

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
}
于 2012-09-03T01:11:09.510 に答える
1

うーん。ええ、それは奇妙です。...そして、これはまだわずかに高速です-単一のデータ引数を許可するという点で matrix() に似ていますが (ただし、スカラーでなければなりません):

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!

とにかく、間違いなく改善の余地があります!

于 2012-08-31T21:17:54.223 に答える