أين يمكنني أن أتعلم كيفية كتابة رمز C لتسريع وظائف R البطيئة؟ [مغلق

StackOverflow https://stackoverflow.com/questions/4106174

  •  29-09-2019
  •  | 
  •  

سؤال

ما هو أفضل مورد لتعلم كيفية كتابة رمز C للاستخدام مع R؟ أنا أعرف عن واجهات النظام واللغة الأجنبية قسم من R extensions ، لكنني أجد أنه من الصعب للغاية الذهاب. ما هي الموارد الجيدة (عبر الإنترنت وغير متصل) لكتابة رمز C للاستخدام مع R؟

للتوضيح ، لا أريد أن أتعلم كيفية كتابة رمز C ، أريد أن أتعلم كيفية دمج R و C. أو من عداد C إلى متجه R؟

هل كانت مفيدة؟

المحلول

حسنًا ، هناك القديم الجيد استخدم المصدر ، لوقا! --- R نفسها لديها الكثير من الرمز C (فعال للغاية) يمكن للمرء أن يدرسها ، ويحتوي Cran على مئات الحزم ، وبعضها من المؤلفين الذين تثق بهم. التي توفر أمثلة حقيقية مختبرة للدراسة والتكيف.

ولكن كما يشتبه جوش ، أنا أميل أكثر نحو C ++ وبالتالي RCPP. كما أن لديها الكثير من الأمثلة.

يحرر: كان هناك كتابان وجدتهما مفيدة:

  • الأول هو Venables و Ripley "برمجة S."على الرغم من أنها تستمر في الأسنان (وكان هناك شائعات عن الإصدار الثاني لسنوات). في ذلك الوقت لم يكن هناك شيء آخر ببساطة.
  • الثاني في الغرف "برنامج لتحليل البيانات"هذا أكثر حداثة ولديه شعور أجمل حول R-وفصلين على تمديد R. استوعب لذلك وحده يستحق سعر القبول.

ومع ذلك ، فإن جون ينمو RCPP (والمساهمة) لأنه يجد المطابقة بين كائنات R وكائنات C ++ (عبر RCPP) أن تكون طبيعية للغاية - والمرجعية تساعد هناك.

تحرير 2: مع سؤال هادلي ، أنا بشكل قوي جدا حثك على النظر في C ++. هناك الكثير من الهراء الذي يجب عليك فعله بـ C-شاق جدًا و يمكن تجنبها جدا. ألق نظرة على RCPP المقص القصيرة. مثال بسيط آخر هو منشور المدونة هذا حيث أظهر أنه بدلاً من القلق حوالي 10 ٪ من الاختلافات (في أحد أمثلة Radford Neal) ، يمكننا الحصول على ثمانين أضعاف يزيد مع C ++ (على ما هو بالطبع مثال مفتعل).

تحرير 3: هناك تعقيد من حيث أنك قد تصطدم بأخطاء C ++ التي هي ، بعبارة معتدل ، يصعب تجنبها. ولكن فقط استخدم RCPP بدلاً من تمديده ، يجب أن تحتاج إلى ذلك. وبين هذا كلفة لا يمكن إنكاره ، فهو يتفوق على المنفعة من الكود البسيط ، أقل من الغلاية ، لا حماية/غير محاسبية ، لا إدارة للذاكرة وما إلى ذلك. YMMV وكل ذلك.

نصائح أخرى

هادلي ،

يمكنك بالتأكيد كتابة رمز C ++ الذي يشبه رمز C.

أفهم ما تقوله عن كون C ++ أكثر تعقيدًا من C. هذا إذا كنت ترغب في إتقان كل شيء: الكائنات ، القوالب ، STL ، برمجة Meta Template ، إلخ ... معظم الناس لا يحتاجون إلى هذه الأشياء ويمكنهم فقط الاعتماد على الآخرين لذلك. إن تنفيذ RCPP معقد للغاية ، ولكن فقط لأنك لا تعرف كيف تعمل الثلاجة ، فهذا لا يعني أنه لا يمكنك فتح الباب والاستيلاء على الحليب الطازج ...

من العديد من المساهمات الخاصة بك إلى R ، ما الذي يثيرني هو أن تجد R مملة إلى حد ما (معالجة البيانات ، الرسومات ، Manipulatio السلسلة ، إلخ ...). حسناً ، كن مستعدًا للعديد من المفاجآت مع واجهة برمجة تطبيقات C الداخلية لـ R. هذا مملة للغاية.

من وقت لآخر ، قرأت أدلة R-Exts أو R-int. هذا يساعد. لكن في معظم الوقت ، عندما أريد حقًا معرفة شيء ما ، أذهب إلى مصدر R ، وكذلك في مصدر الحزم التي كتبها EG Simon (عادة ما يكون هناك الكثير لنتعلمه هناك).

تم تصميم RCPP لجعل هذه الجوانب الشاقة من واجهة برمجة التطبيقات تختفي.

يمكنك الحكم على نفسك بما تجده أكثر تعقيدًا أو محفوظًا ، وما إلى ذلك ... استنادًا إلى بعض الأمثلة. تنشئ هذه الوظيفة متجهًا للأحرف باستخدام 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 ;
}

كما قال ديرك ، هناك أمثلة أخرى على العديد من المقالات القصيرة. عادةً ما نوجه الأشخاص نحو اختبارات وحدتنا لأن كل منهم يختبر جزءًا محددًا للغاية من الكود وهو توضيحي ذاتي إلى حد ما.

من الواضح أنني منحازة هنا ، لكنني أوصي بالتعرف على RCPP بدلاً من تعلم API C لـ R ، ثم أتيت إلى القائمة البريدية إذا كان هناك شيء غير واضح أو لا يبدو قابلاً للتنفيذ مع RCPP.

على أي حال ، نهاية الملعب المبيعات.

أعتقد أن كل هذا يتوقف عن نوع التعليمات البرمجية التي تريد كتابتها في النهاية.

رومان

Hadley: لسوء الحظ ، ليس لدي موارد محددة في الاعتبار لمساعدتك في البدء في C ++. لقد التقطتها من كتب Scott Meyers (فعالة C ++ ، أكثر فعالية C ++ ، إلخ ...) ولكن هذه ليست حقًا ما يمكن أن يسميه المرء تمهيديًا.

نستخدم واجهة .Call بشكل حصري تقريبًا لاستدعاء رمز C ++. القاعدة سهلة بما فيه الكفاية:

  • يجب أن تُرجع وظيفة C ++ كائن R. جميع الأشياء r هي sexp.
  • تستغرق وظيفة C ++ بين 0 و 65 R كائنات كإدخال (مرة أخرى sexp)
  • يجب أن يتم الإعلان عن ذلك (ليس حقًا ، ولكن يمكننا حفظ هذا في وقت لاحق) مع رابط C ، إما مع خارجي "ج" أو ال RCPPEXPORT الاسم المستعار أن RCPP يحدد.

لذلك يتم الإعلان عن وظيفة .Call مثل هذا في ملفات رأس:

#include <Rcpp.h>

RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;

وتنفيذ مثل هذا في ملف .cpp:

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

ليس هناك الكثير الذي يجب معرفته حول A API R لاستخدام RCPP.

معظم الناس يريدون فقط التعامل مع المتجهات الرقمية في RCPP. يمكنك القيام بذلك مع فئة NumericVector. هناك عدة طرق لإنشاء متجه رقمي:

من كائن موجود تنقله من R:

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

مع القيم المعطاة باستخدام :: إنشاء وظيفة ثابتة:

 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

ثم بمجرد أن يكون لديك متجه ، فإن الشيء الأكثر فائدة هو استخراج عنصر واحد منه. يتم ذلك مع المشغل [] ، مع الفهرسة القائمة على 0 ، لذلك على سبيل المثال ، يلخص قيم متجه رقمي شيء من هذا القبيل:

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 ، والتحقق من المقالات القصيرة ، واختبارات الوحدة ، والعودة إلينا في القائمة البريدية. نحن سعداء دائمًا بالمساعدة.

jbremnant: هذا صحيح. تنفذ فصول RCPP شيئًا قريبًا من نمط RAII. عند إنشاء كائن RCPP ، يتخذ المنشئ التدابير المناسبة لضمان حماية كائن R الأساسي (SEXP) من جامع القمامة. المدمر يسحب الحماية. تم شرح هذا في RCPP-Intrduction المقالات القصيرة. يعتمد التنفيذ الأساسي على وظائف R API r_preservebject و R_ReleaseObject

هناك بالفعل عقوبة أداء بسبب تغليف C ++. نحاول الحفاظ على هذا كحد أدنى من خلال الإطار ، وما إلى ذلك ... العقوبة صغيرة ، وعندما تأخذ في الاعتبار المكسب من حيث الوقت الذي يستغرقه الكتابة والحفاظ على الكود ، فهذا ليس ذا صلة.

استدعاء وظائف R من وظيفة فئة RCPP أبطأ من استدعاء Eval مباشرة مع C API. وذلك لأننا نتخذ الاحتياطات ونلتف استدعاء الوظيفة في كتلة TryCatch بحيث نلتقط أخطاء R وترويجها إلى استثناءات C ++ بحيث يمكن التعامل معها باستخدام المحاولة القياسية في C ++.

يرغب معظم الناس في استخدام المتجهات (خاصة NumericVector) ، والعقوبة صغيرة جدًا مع هذه الفئة. يحتوي دليل الأمثلة/incholveBenchmarks على العديد من المتغيرات من وظيفة الالتزام سيئة السمعة من R-Exts و Vignette لها نتائج قياسية. اتضح أن RCPP يجعلها أسرع من الرمز القياسي الذي يستخدم API R.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top