1. ホーム
  2. performance

[解決済み] Rでループが遅いのはなぜですか?

2023-03-12 13:37:16

質問

でループが遅いのは知っています。 R でループが遅いことも、代わりにベクトル化された方法で物事を行うよう試みるべきであることも知っています。

しかし、なぜでしょう?なぜループは遅いのか、そして apply は速いのでしょうか? apply はいくつかのサブファンクションを呼び出すので、速いとは思えません。

更新しました。 すみません、質問の仕方が悪かったです。ベクトル化を混同していました。 apply . 私の質問は、そうであるべきでした。

"なぜベクトル化は速いのでしょうか?

どのように解決するのですか?

Rのループが遅いのは、どんなインタプリタ言語でも遅いのと同じ理由です。 演算が多くの余分な荷物を運んでくるからです。

見てください。 R_execClosureeval.c (を呼び出すために呼ばれる関数です(これは ユーザー定義関数を呼び出すために呼び出される関数です)。この関数は100行近くあり、あらゆる種類の操作を実行します。 実行環境の作成、環境への引数の割り当てなど、あらゆる種類の操作を行います。 実行環境の構築、環境への引数の割り当てなどです。

C 言語で関数を呼び出すとき、どれだけ少ないか考えてみてください (引数をスタックにプッシュする、ジャンプする、引数をポップする)。 スタックにプッシュし、ジャンプし、引数をポップする)。

というわけで、このようなタイミングになるわけです(joranさんがコメントで指摘されているように。 実際には apply の内部 C ループが高速化されているのです。 mean の内部 C ループです。 apply は普通の古いRのコードです)。

A = matrix(as.numeric(1:100000))

ループを使用。0.342秒です。

system.time({
    Sum = 0
    for (i in seq_along(A)) {
        Sum = Sum + A[[i]]
    }
    Sum
})

和を用いる:測り知れないほど小さい。

sum(A)

少し気になるのは、漸近的に、ループはちょうど として sum ループは漸近的には と同じで、遅くなる理由はありません。 ループは反復するたびに余分な作業をしているだけです。

そこで、考えてみましょう。

# 0.370 seconds
system.time({
    I = 0
    while (I < 100000) {
        10
        I = I + 1
    }
})

# 0.743 seconds -- double the time just adding parentheses
system.time({
    I = 0
    while (I < 100000) {
        ((((((((((10))))))))))
        I = I + 1
    }
})

(その例を発見したのは ラドフォード ニール )

なぜなら ( は演算子であり、実際には使用するたびに名前のルックアップが必要だからです。

> `(` = function(x) 2
> (3)
[1] 2

あるいは一般的に、(どんな言語でも)解釈された操作はより多くのステップを必要とします。もちろん、これらのステップには利点もあります。 を行う というのは ( のトリックをCで行うことができます。