11

私は現在クラスシステムを使用しているRパッケージを持っていますS3.2つの異なるクラスとplotlogLikおよびupdate(モデル式の更新用)などの一般的なS3関数用のいくつかのメソッドがあります。if/elseの 2 つの引数に基づく継承またはディスパッチがないという事実により、すべての有効性チェックと構造体でコードがより複雑になったためS3、パッケージを に変換することを考え始めましたS4S3しかし、その後、 vsの長所と短所について読み始めましたが、S4もうよくわかりません。S3 と S4 の効率性の問題に関するR ブロガーのブログ記事を見つけました。これは 5 年前のことなので、同じことをテストしました。

library(microbenchmark)
setClass("MyClass", representation(x="numeric"))
microbenchmark(structure(list(x=rep(1, 10^7)), class="MyS3Class"),
               new("MyClass", x=rep(1, 10^7)) )
Unit: milliseconds
                                                   expr
 structure(list(x = rep(1, 10^7)), class = "MyS3Class")
                       new("MyClass", x = rep(1, 10^7))
       min       lq   median       uq      max neval
 148.75049 152.3811 155.2263 159.8090 323.5678   100
  75.15198 123.4804 129.6588 131.5031 241.8913   100

したがって、この単純な例でS4は、実際には少し高速でした。次に、 vsの使用に関するSO の質問を読みました。特に@joshua-ulrichの答えは、S3S4S3S4

スロットの変更には、完全なオブジェクトのコピーが必要です

モデルの対数尤度を最適化するときに、反復ごとにオブジェクトを更新するという私のケースを考えると、それは大きな問題のように感じます。グーグルで調べた後、R 3.0.0 で変更されているように見える、この問題に関するJohn Chambers の投稿を見つけました。

したがってS4、コードを明確にするためにクラスを使用することは有益だと思いますが (たとえば、メイン モデル クラスから継承するより多くのクラス)、および有効性チェックなどのために、すべての作業の価値があるかどうか疑問に思っています。パフォーマンス?では、パフォーマンスに関して、 と の間に実際のパフォーマンスの違いはS3ありS4ますか? 他に考慮すべきパフォーマンスの問題はありますか? それとも、この問題について一般的に何かを言うことさえ可能ですか?

編集: @DWin と @g-grothendieck が示唆したように、上記のベンチマークでは、既存のオブジェクトのスロットが変更された場合を考慮していません。したがって、実際のアプリケーションにより関連する別のベンチマークを次に示します (例の関数は、モデル内のいくつかの要素の get/set 関数である可能性があり、対数尤度を最大化するときに変更されます)。

objS3<-structure(list(x=rep(1, 10^3), z=matrix(0,10,10), y=matrix(0,10,10)),
                 class="MyS3Class")
fnS3<-function(obj,a){
  obj$y<-a
  obj
}

setClass("MyClass", representation(x="numeric",z="matrix",y="matrix"))
objS4<-new("MyClass", x=rep(1, 10^3),z=matrix(0,10,10),y=matrix(0,10,10))
fnS4<-function(obj,a){ 
  obj@y<-a
  obj
}

a<-matrix(1:100,10,10)
microbenchmark(fnS3(objS3,a),fnS4(objS4,a))
Unit: microseconds
           expr    min     lq median     uq    max neval
 fnS3(objS3, a)  6.531  7.464  7.932  9.331 26.591   100
 fnS4(objS4, a) 21.459 22.393 23.325 23.792 73.708   100

ベンチマークは、64 ビット Windows 7 の R 2.15.2 で実行されます。したがって、ここでS4は明らかに遅いです。

4

3 に答える 3

8
  • まず、S4 クラスの S3 メソッドを簡単に作成できます。

    > extract <- function (x, ...) x@x
    > setGeneric ("extr4", def=function (x, ...){})
    [1] "extr4"
    > setMethod ("extr4", signature= "MyClass", definition=extract)
    [1] "extr4"
    > `[.MyClass` <- extract
    > `[.MyS3Class` <- function (x, ...) x$x
    > microbenchmark (objS3[], objS4 [], extr4 (objS4), extract (objS4))
    Unit: nanoseconds
               expr   min      lq  median      uq   max neval
            objS3[]  6775  7264.5  7578.5  8312.0 39531   100
            objS4[]  5797  6705.5  7124.0  7404.0 13550   100
       extr4(objS4) 20534 21512.0 22106.0 22664.5 54268   100
     extract(objS4)   908  1188.0  1328.0  1467.0 11804   100
    

編集: Hadley のコメントにより、実験を次のように変更しplotます。

> `plot.MyClass` <- extract
> `plot.MyS3Class` <- function (x, ...) x$x
> microbenchmark (plot (objS3), plot (objS4), extr4 (objS4), extract (objS4))
Unit: nanoseconds
           expr   min      lq median      uq     max neval
    plot(objS3) 28915 30172.0  30591 30975.5 1887824   100
    plot(objS4) 25353 26121.0  26471 26960.0  411508   100
   extr4(objS4) 20395 21372.5  22001 22385.5   31359   100
 extract(objS4)   979  1328.0   1398  1677.0    3982   100

S4 メソッドの場合plot:

    plot(objS4) 19835 20428.5 21336.5 22175.0 58876   100

はい、[非常に高速なディスパッチ メカニズムを備えています (これは良いことです。なぜなら、抽出と対応する置換関数は最も頻繁に呼び出されるメソッドの 1 つだと思うからです。しかし、いいえ、S4 ディスパッチは S3 ディスパッチよりも遅くはありません。


ここで、S4 オブジェクトの S3 メソッドは、S3 オブジェクトの S3 メソッドと同じくらい高速です。ただし、ディスパッチなしで呼び出した方が高速です。

  • as.matrixまたはなど、S3 の方がはるかにうまく機能するものがあります。as.data.frame
    何らかの理由で、これらを S3 として定義することは、たとえばそのlm (formula, objS4)ままで機能することを意味します。as.data.frameこれは、S4 メソッドとして定義されていると機能しません。

  • debugまた、S3 メソッドを呼び出す方がはるかに便利です。

  • 2 番目の引数でディスパッチするなど、S3 では機能しないものもあります。

  • パフォーマンスが著しく低下するかどうかは、クラス、つまり、構造体の種類、オブジェクトの大きさ、メソッドの呼び出し頻度によって明らかに異なります。メソッドディスパッチの数 μs は、ms または s の計算では問題になりません。しかし、関数が何十億回も呼び出される場合、μs は問題になります。

  • 頻繁に呼び出されるいくつかの関数 ( [) で顕著なパフォーマンスの低下を引き起こした原因の 1 つは、S4 検証 ( で行われたかなりの数のチェックvalidObject) ですが、それができてよかったので、使用しています。このステップを飛ばす。

  • 大規模なデータがあり、参照渡しがパフォーマンスに役立つ場合は、参照クラスを確認することをお勧めします。これまで彼らと実際に仕事をしたことがないので、これについてコメントすることはできません.

于 2013-03-23T20:47:48.577 に答える
2

パフォーマンスが気になる場合は、ベンチマークを行ってください。多重継承または多重ディスパッチが本当に必要な場合は、S4 を使用してください。それ以外の場合は、S3 を使用します。

于 2013-03-23T21:44:40.030 に答える
1

(これは「意見を引き出す可能性が高い質問」の境界にかなり近いですが、コードとデータ、および有用な引用を提供した重要な問題であると考えているため、閉じる投票がないことを願っています。)

私はプログラミングの S4 モデルを本当に理解したことがないことを認めます。しかし、Chambers 氏の投稿で言われていたことは@<-、スロットの割り当てはクロージャーとしてではなくプリミティブとして再実装されているため、1 つのコンポーネントが変更されたときにオブジェクトの完全なコピーを必要としないということです。そのため、以前の状態は R 3.0.0 ベータ版で変更されます。私のマシン (R 3.0.0 ベータ版を実行している 5 年前の MacPro) では、相対的な違いはさらに大きかった。ただし、複数のスロットを持つ名前付きオブジェクトの既存のコピーを変更していないため、これは必ずしも良いテストではないと思いました。

res <-microbenchmark(structure(list(x=rep(1, 10^7)), class="MyS3Class"),
                new("MyClass", x=rep(1, 10^7)) )
summary(res)[ ,"median"]
#[1] 145.0541 103.4064

あなたの脳の構造は私のものよりも柔軟であり、John Chambers 以外の 2 人を挙げれば Douglas Bates と Martin Maechler という非常に賢い人がたくさんいるので、S4 を使うべきだと思います。彼らは重い処理を必要とするパッケージに S4 ​​メソッドを使用しています. Matrix と lme4 パッケージはどちらも、重要な機能に S4 ​​メソッドを使用します。

于 2013-03-23T19:37:52.773 に答える