1

lapply関数またはループでガベージ コレクションを実行する最速の方法は何ですか? 私には明白に思えることは、物事を非常に遅くします。私はそれを間違っていますか?より速い方法はありますか?

x <- 1:10000
system.time(xx <- lapply(1:length(x), function(xi) sum(x[1:xi])))
 user  system elapsed 
   0.02    0.00    0.02 
system.time(xx <- lapply(1:length(x), function(xi) sum(x[1:xi], invisible(gc(v=FALSE)))))
   user  system elapsed 
  22.49    0.00   22.57 # a thousand times increase in time taken!!

私の実際のユースケースでは、関数はもう少し複雑で、gcインスタンスごとに失敗します。RAM の多いマシンに切り替えることもできますが、便利ではありません。より高速なgc方法が利用できるかどうか知りたいです。

更新マーティン・モーガンの提案に従って、物事をわずかに再配置すると、速度がlapplyなしに近くなりますgc(現在は別のマシンで作業しているため、タイミングが上記とは異なります):

x <- 1:10000
system.time(x1 <- lapply(1:length(x), function(xi) sum(x[1:xi])))
   user  system elapsed 
   3.47    0.00    3.56 
# define a function to make a sequence of a function followed by gc
sum_gc <- function(x) sum(x); invisible(gc(v=FALSE))
system.time(x3 <- lapply(1:length(x), function(xi) sum_gc(x[1:xi])))
   user  system elapsed 
   3.52    0.02    3.56 
4

1 に答える 1

3

本当の答えではありませんが、コメントよりも長くなります。ベン、これ

fun0 = function(x) sum(x, gc())

「x と gc() によって返される値」の合計を計算する関数を定義します。これ

fun1 = function(x) sum(x); gc()

x の合計を返す関数を定義します。gc()関数が定義された後に実行されますが、関数定義の一部ではありません。

fun2 = function(x) {
    result = sum(x)
    gc()
    result
}

x の合計を計算し、result関数内に存在する変数に保存する関数を定義します。次に、関数を評価しますgc()result次に、 に含まれる値、つまり x の合計を返します。時間に加えて結果を比較する価値があります

test_case = 1:5
identical(sum(test_case), fun0(test_case))  # FALSE
identical(sum(test_case), fun1(test_case))  # TRUE, but no garbage collection
identical(sum(test_case), fun2(test_case))  # TRUE

最初に評価された後、呼び出しても実際には何も達成gc()されfun2ません。fun2割り当てられているがシンボルに関連付けられていないメモリはないため、ガベージを収集する必要はありません。これは、メモリを割り当てて使用し、それへの参照を削除してから、ガベージ コレクションを実行してメモリを解放する場合です。

fun3 = function(x) {
   m = rnorm(length(x))
   result = sum(m * x)
   rm(m)
   gc()
   result
}

しかし、明示的なガベージ コレクションは、ここでは何も役に立ちません。ガベージ コレクタは、R が使用可能なメモリよりも多くのメモリを必要とする場合に自動的に実行されます。が複数回呼び出された場合fun3、シンボルによって参照されなくなった各呼び出し内で使用されるメモリが存在するため、ガベージ コレクターが自動的に実行されるときに収集されます。直接呼び出すことによりgc()、単純なガベージ コレクション戦略 (常に実行する) が R の戦略 (より多くのメモリが必要なときに実行する) よりも優れていると主張しています。

どちらができる可能性がありますか(より良いガベージコレクターを作成してください)。

しかし、ここではそうではありません。

パフォーマンスやメモリの問題に直面したときは、一歩下がってアルゴリズムと実装を確認することで利益が得られることが多いと述べました。これが「おもちゃ」の例であることはわかっていますが、とにかく見てみましょう。計算しているのは、x の要素の累積和です。私はあなたの実装を次のように書いていただろう

fun4 = function(i, x) sum(x[seq_len(i)])
sapply(seq_along(test_case), fun4, test_case)

与える

> x0 <- sapply(seq_along(test_case), fun4, test_case)
> x0
[1]  1  3  6 10 15

しかし、R には、cumsumメモリと速度の両方の点でこれをより効率的に行う機能があります。

> x1 <- cumsum(test_case)
> identical(x0, x1)
[1] TRUE
> test_case = seq_len(10000)
> system.time(x0 <- sapply(seq_along(test_case), fun4, test_case))
   user  system elapsed 
  2.508   0.000   2.517 
> system.time(x1 <- cumsum(test_case))
   user  system elapsed 
  0.004   0.000   0.002 
于 2013-04-20T21:35:31.737 に答える