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


115

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

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

回答:


71

さて、古き良き時代があります。--- R自体には(非常に効率的な)Cコードが豊富にあり、CRANには数百のパッケージが含まれています。これは、研究および適応するための実際のテスト済みの例です。

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

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

  • 最初は、VenablesとRipleyの「Sプログラミング」です。当時、他には何もありませんでした。
  • Chambersの「Software for Data Analysis」の2番目は、より最近であり、R中心の感覚がより優れています。Rの拡張に関する2つの章があります。CとC ++の両方について言及します。加えて、ジョンは私がダイジェストでやったことのために私を細断するので、それだけで入場料の価値があります。

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

編集2: Hadleyの再フォーカスされた質問で、私はC ++を検討することを強く強く勧めます。あなたがCと関係しなければならない多くのボイラープレートのナンセンスがあります---非常に退屈で非常に回避可能です。Rcppの紹介ビネットをご覧ください。別の簡単な例はこのブログ投稿で、10%の違いを心配する代わりに(Radford Nealの例の1つ)、C ++で80倍の増加が得られることを示しています(当然の例ですが)。

編集3: C ++のエラーが発生する可能性があるという複雑さがあります。しかし、Rcppを拡張するのではなく、単に使用するだけであれば、ほとんど必要ありません。そして、このコストは否定できませんが、よりシンプルなコード、ボイラープレートが少ない、PROTECT / UNPROTECTがない、メモリ管理がないなどの利点によってはるかに上回っています。 C ++を書くよりも。YMMVなど。


「use Rcpp」という答えが得られると期待していました;)Cの代わりにC ++を使用することのデメリットを詳しく説明できれば非常に便利です。C++はCよりもはるかに複雑であるように思われます。これは使いにくくなりますか?(または実際には、Cに非常に似たC ++コードを記述できますか?)また、既存のC APIに慣れていない新規ユーザーを対象とした参考資料をもっといただければ幸いです。
ハドレーは

2
編集3を参照してください。そうです。マイヤーズはC ++を「4つのパラダイム」言語と呼び、4つすべてを使用する必要はありません。それを「ちょうど良いC」として使用し、RcppをRへの接着剤として使用することは、まったく問題ありません。誰もあなたにスタイルを強制しません-これはJavaではありません;-)
Dirk Eddelbuettel

@Dirk:精緻化のためのthx。ここではC ++の代わりにCが一般的に使用されているため、以前に私たちのオフィスで問題が発生しました。C ++でのCの使用が有益なのはいつですか、または単に「Cではなく、常にC ++」と言っていますか?
Joris Meys、2010年

ハドリー:クール。私たちはあなたのフィードバックに非常に興味があります。rcpp-develに参加してください。私たちは短いドキュメンテーションであることを知っています-しかし、新鮮な目が途方もなく役立つかもしれません。
Dirk Eddelbuettel、2010年

6
@hadleyは、速度の向上が期待できることを意味しggplotますか?
aL3xa 2011

56

ハドリー、

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

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

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

時々、R-extsまたはR-intsのマニュアルを読みました。これは役立ちます。しかし、ほとんどの場合、私が何かについて本当に知りたいときは、Rソースに行きます。また、例えばSimonによって書かれたパッケージのソースに行きます(通常、そこで学ぶことはたくさんあります)。

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で実行できないと思われる場合は、メーリングリストに参加してください。

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

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

ロマン


2
「Rcppは、APIのこれらの面倒な側面がなくなるように設計されています」=まさに私が探しているもの。ありがとう!Cに精通していてRcppを使用したい人にとって、v。簡単なC ++入門書が本当に役立つでしょう。
ハドリー

いいですね、Rcppの短い例で私は売れました。allocXXとUNPROTECT(1)は、スマートポインターがリソースを管理する方法と同じように処理されると想定しています。すなわち、RAII。バニラCのAPIよりもRcppを使用することにより、パフォーマンスが著しく低下することはありますか?
jbremnant 2010年

Rcppの概要で、ベンチマークの例(sources / installedパッケージにも含まれています)を使用して対処します。つまり、ペナルティはまったくありません。
Dirk Eddelbuettel、2010

29

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

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

  • C ++関数はRオブジェクトを返す必要があります。すべてのRオブジェクトはSEXPです。
  • C ++関数は0から65のRオブジェクトを入力として受け取ります(これもSEXP)。
  • これは(実際にはそうではありませんが、後で保存することができます)extern "C"またはRcppが定義するRcppExportエイリアスを使用して、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に依存するパッケージで人々が何をしているのかを調べ、ビネットとユニットテストをチェックして、メーリングリストに戻ってください。いつでもお手伝いさせていただきます。


20

@jbremnant:そうです。Rcppクラスは、RAIIパターンに近いものを実装します。Rcppオブジェクトが作成されると、コンストラクターは適切な対策を講じて、基になるRオブジェクト(SEXP)がガベージコレクターから保護されるようにします。デストラクタは保護を取り消します。これは、Rcpp導入ビネットで説明されています。基礎となる実装は、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を使用するベンチマークコードよりも高速であることがわかりました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.