web-dev-qa-db-ja.com

遅いR関数を高速化するCコードの記述方法はどこで習得できますか?

Rで使用するCコードを記述する方法を学ぶための最良のリソースは何ですか? R拡張機能の システムおよび外国語インターフェース セクションについては知っていますが、かなり難しいと思います。 Rで使用するCコードを作成するための優れたリソース(オンラインとオフラインの両方)は何ですか?

明確にするために、Cコードの書き方を学びたくありません。RとCをより適切に統合する方法を学びたいと思います。たとえば、C整数ベクトルからR整数ベクトルに(またはその逆に)変換する方法を学びますまたはCスカラーからRベクトルへ?

112
hadley

さて、古き良き時代がありますソース、ルークを使用してください!--- R自体には、(非常に効率的な)Cコードがたくさんあります。何百ものパッケージがあり、信頼できる作者からのパッケージもあります。これは、調査および適応するための実際のテスト済みの例です。

しかし、ジョシュが疑ったように、私はC++に傾いているため、 Rcpp です。例もたくさんあります。

編集:参考になった2冊の本がありました。

  • 最初のものはVenablesとRipleyの "S Programming"ですが、歯が長くなっています(そして、2年目の噂が何年も続いています) )。当時、他には何もありませんでした。
  • Chambersの2番目の「Software for Data Analysis」は、より最近のものであり、R中心の感じがはるかに良く、拡張に関する2つの章があります。 R. CとC++の両方が言及される。さらに、ジョンは私が ダイジェスト でやったことで私を細断します。

そうは言っても、ジョンは Rcpp (そして貢献)が好きになり、RオブジェクトとC++オブジェクト( Rcpp を介して)の間の一致が非常に自然であるとわかりました-そしてReferenceClasses助けて。

編集2:Hadleyの再フォーカスされた質問で、私は非常に強く C++を検討してください。 C ---非常に退屈で非常に回避可能で行う必要のあるボイラープレートのナンセンスはたくさんあります。 Rcpp-introduction vignette をご覧ください。別の簡単な例は このブログ投稿 であり、(Radford Nealの例の1つで)10%の違いを心配する代わりに、eightyfoldはC++で増加します(当然の例ですが)。

編集3:C++エラーが発生する可能性があるという複雑さがあります。しかし、拡張するのではなく、Rcppを使用するだけで、ほとんど必要ありません。そして、このcostは否定できませんが、benefitに大きく劣っていますよりシンプルなコード、ボイラープレートが少ない、PROTECT/UNPROTECT、メモリ管理などがないpp。Doug Batesは昨日、C++とRcppはC++を書くよりもRを書くことに似ていると述べた。 YMMVなど。

68

ハドリー、

Cコードに似たC++コードを確実に作成できます。

私はあなたがC++がCよりも複雑であることについてあなたの言うことを理解しています。これはすべてをマスターしたい場合です:オブジェクト、テンプレート、STL、テンプレートメタプログラミングなど...ほとんどの人はこれらのものを必要とせず、他のものに頼ることができますそれに。 Rcppの実装は非常に複雑ですが、冷蔵庫の仕組みがわからないからといって、ドアを開けて新鮮な牛乳をつかむことができないというわけではありません...

Rへの多くの貢献から、私を驚かせるものは、Rがいくらか退屈だと思うことです(データ操作、グラフィックス、文字列操作など...)。 Rの内部C APIでさらに多くの驚きに備えてください。これは非常に退屈な作業です。

時々、R-extsまたはR-intsのマニュアルを読みました。これは役立ちます。しかし、ほとんどの場合、私が本当に何かを知りたいときは、Rのソースに行きます。サイモン(通常、そこで学ぶことはたくさんあります)。

Rcppは、APIのこれらの面倒な側面を解消するように設計されています。

いくつかの例に基づいて、複雑で難読化されているものなどを自分で判断できます。この関数は、C APIを使用して文字ベクトルを作成します。

SEXP foobar(){
  SEXP ab;
  PROTECT(ab = allocVector(STRSXP, 2));
  SET_STRING_ELT( ab, 0, mkChar("foo") );
  SET_STRING_ELT( ab, 1, mkChar("bar") );
  UNPROTECT(1);
}

Rcppを使用すると、同じ関数を次のように記述できます。

SEXP foobar(){
   return Rcpp::CharacterVector::create( "foo", "bar" ) ;
}

または:

SEXP foobar(){
   Rcpp::CharacterVector res(2) ;
   res[0] = "foo" ;
   res[1] = "bar" ;
   return res ;
}

ダークが言ったように、いくつかのビネットには他の例があります。私たちは通常、ユニットテストに人々を向けます。なぜなら、それぞれがコードの非常に特定の部分をテストし、ある程度自明だからです。

私は明らかにここに偏っていますが、RのC APIを学ぶのではなく、Rcppに慣れることをお勧めします。不明な点がある場合や、Rcppで実行できないようであれば、メーリングリストに参加してください。

とにかく、売り込みの終わりです。

最終的には、どのようなコードを記述したいかによります。

ロマン

54
Romain Francois

@hadley:残念ながら、C++を使い始めるのに役立つ具体的なリソースはありません。私はスコット・マイヤーズの本(効果的なC++、より効果的なC++など)からそれを取り上げましたが、これらは実際に紹介と呼ぶことができるものではありません。

ほとんどの場合、.Callインターフェイスを使用してC++コードを呼び出します。ルールはとても簡単です:

  • C++関数はRオブジェクトを返す必要があります。すべてのRオブジェクトはSEXPです。
  • C++関数は、0〜65のRオブジェクトを入力として受け取ります(これもSEXP)。
  • (実際にはそうではないが、後で保存できる)extern "C"またはRcppExport Rcppが定義するエイリアスを使用して、Cリンケージで宣言する必要がある.

したがって、.Call関数は、いくつかのヘッダーファイルで次のように宣言されます。

#include <Rcpp.h>

RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;

このように.cppファイルに実装します。

SEXP foo( SEXP x1, SEXP x2 ){
   ...
}

Rcppを使用するためのR APIについて知っておくべきことはこれ以上ありません。

ほとんどの人はRcppで数値ベクトルのみを扱いたいと思っています。これはNumericVectorクラスで行います。数値ベクトルを作成する方法はいくつかあります。

Rから受け継いだ既存のオブジェクトから:

 SEXP foo( SEXP x_) {
    Rcpp::NumericVector x( x_ ) ;
    ...
 }

:: create static関数を使用して指定された値で:

 Rcpp::NumericVector x = Rcpp::NumericVector::create( 1.0, 2.0, 3.0 ) ;
 Rcpp::NumericVector x = Rcpp::NumericVector::create( 
    _["a"] = 1.0, 
    _["b"] = 2.0, 
    _["c"] = 3
 ) ;

所定のサイズのもの:

 Rcpp::NumericVector x( 10 ) ;      // filled with 0.0
 Rcpp::NumericVector x( 10, 2.0 ) ; // filled with 2.0

次に、ベクターを取得したら、ベクターから1つの要素を抽出するのが最も便利です。これは、0ベースのインデックス付けを伴うoperator []を使用して行われるため、たとえば、数値ベクトルの値の合計は次のようになります。

SEXP sum( SEXP x_ ){
   Rcpp::NumericVector x(x_) ;
   double res = 0.0 ;
   for( int i=0; i<x.size(), i++){
      res += x[i] ;
   }
   return Rcpp::wrap( res ) ;
}

しかし、Rcppシュガーを使用すると、これをよりうまく実行できます。

using namespace Rcpp ;
SEXP sum( SEXP x_ ){
   NumericVector x(x_) ;
   double res = sum( x ) ;
   return wrap( res ) ;
}

前に言ったように、それはあなたがどんな種類のコードを書きたいかによります。 Rcppに依存するパッケージで人々が何をしているのかを調べ、ビネット、単体テストを確認し、メーリングリストに戻ってください。いつでもお手伝いさせていただきます。

28
Romain Francois

@jbremnant:そうです。 Rcppクラスは、RAIIパターンに近いものを実装します。 Rcppオブジェクトが作成されると、コンストラクターは適切な対策を講じて、基になるRオブジェクト(SEXP)がガベージコレクターから保護されるようにします。デストラクタは保護を取り消します。これは Rcpp-intrduction ビネットで説明されています。基礎となる実装は、R API関数R_PreserveObjectおよびR_ReleaseObjectに依存しています

C++カプセル化により、実際にパフォーマンスが低下します。インライン化などでこれを最小限にしようとします...ペナルティは小さく、コードの作成と保守にかかる時間の面での利益を考慮に入れると、それほど重要ではありません。

Rcppクラスの関数からのR関数の呼び出しは、C APIでevalを直接呼び出すよりも遅くなります。これは、予防策を講じて関数呼び出しをtryCatchブロックにラップし、RエラーをキャプチャしてC++例外にプロモートして、C++の標準のtry/catchを使用して処理できるようにするためです。

ほとんどの人はベクトル(特にNumericVector)を使用することを望んでおり、このクラスではペナルティは非常に小さくなります。 examples/ConvolveBenchmarksディレクトリには、R-extsの悪名高い畳み込み関数のバリアントがいくつか含まれており、ビネットにはベンチマーク結果があります。 Rcppは、R APIを使用するベンチマークコードよりも高速であることがわかりました。

19
Romain Francois