Di mana saya dapat mempelajari cara menulis kode C untuk mempercepat fungsi R yang lambat? [Tutup]

115

Apa sumber daya terbaik untuk mempelajari cara menulis kode C untuk digunakan dengan R? Saya tahu tentang sistem dan bagian antarmuka bahasa asing dari ekstensi R, tetapi saya merasa cukup sulit. Apa sumber daya yang baik (baik online maupun offline) untuk menulis kode C untuk digunakan dengan R?

Untuk memperjelas, saya tidak ingin belajar bagaimana menulis kode C, saya ingin belajar bagaimana mengintegrasikan R dan C.Misalnya, bagaimana cara mengonversi dari vektor integer C ke vektor integer R (atau sebaliknya) atau dari skalar C ke vektor R?

hadley
sumber

Jawaban:

71

Nah ada yang baik tua Gunakan sumbernya, Luke! --- R sendiri memiliki banyak kode C (sangat efisien) yang dapat dipelajari, dan CRAN memiliki ratusan paket, beberapa dari penulis yang Anda percaya. Itu memberikan contoh nyata dan teruji untuk dipelajari dan diadaptasi.

Tapi seperti yang diduga Josh, saya lebih condong ke C ++ dan karenanya Rcpp . Ini juga memiliki banyak contoh.

Edit: Ada dua buku yang menurut saya berguna:

  • Yang pertama adalah " Pemrograman S " Venables dan Ripley meskipun sudah lama di gigi (dan ada rumor edisi ke-2 selama bertahun-tahun). Pada saat itu tidak ada yang lain.
  • Yang kedua dalam " Software for Data Analysis " Chambers yang jauh lebih baru dan memiliki nuansa R-centric yang jauh lebih baik - dan dua bab tentang perluasan R. Baik C dan C ++ disebutkan. Plus, John mencabik-cabik saya untuk apa yang saya lakukan dengan intisari sehingga itu saja sepadan dengan harga tiket masuk.

Yang mengatakan, John semakin menyukai Rcpp (dan berkontribusi) saat ia menemukan kecocokan antara objek R dan objek C ++ (melalui Rcpp ) menjadi sangat alami - dan ReferenceClasses membantu di sana.

Sunting 2: Dengan pertanyaan yang difokuskan pada Hadley, saya sangat menyarankan Anda untuk mempertimbangkan C ++. Ada begitu banyak omong kosong yang harus Anda lakukan dengan C --- sangat membosankan dan sangat bisa dihindari . Lihat sketsa pengenalan Rcpp . Contoh sederhana lainnya adalah posting blog ini di mana saya menunjukkan bahwa alih-alih mengkhawatirkan perbedaan 10% (dalam salah satu contoh Radford Neal) kita bisa mendapatkan peningkatan delapan kali lipat dengan C ++ (tentu saja merupakan contoh yang dibuat-buat).

Sunting 3: Ada kerumitan karena Anda mungkin mengalami kesalahan C ++ yang, secara halus, sulit untuk dilakukan. Tetapi untuk hanya menggunakan Rcpp daripada memperpanjangnya, Anda seharusnya tidak membutuhkannya. Dan sementara biaya ini tidak dapat disangkal, itu jauh dikalahkan oleh manfaat kode yang lebih sederhana, lebih sedikit boilerplate, tidak ada PROTECT / UNPROTECT, tidak ada manajemen memori, dll. Hal. Doug Bates baru kemarin menyatakan bahwa dia menemukan C ++ dan Rcpp lebih seperti menulis R daripada menulis C ++. YMMV dan semua itu.

Dirk Eddelbuettel
sumber
Saya berharap saya akan mendapatkan jawaban "gunakan Rcpp";) Akan sangat berguna jika Anda bisa menjelaskan kerugian menggunakan C ++ daripada C. Salah satu yang utama tampaknya adalah bahwa C ++ jauh lebih kompleks daripada C - ini membuatnya lebih sulit untuk digunakan? (Atau dalam praktiknya, dapatkah Anda menulis kode C ++ yang sangat mirip dengan C?) Saya juga akan menghargai lebih banyak materi referensi yang ditujukan untuk pengguna baru yang tidak terbiasa dengan C api yang ada.
hadley
2
Lihat Edit 3 dan ya, Anda bisa . Meyers menyebut C ++ sebagai bahasa 'empat paradigma' dan Anda tidak harus menggunakan keempatnya. Menggunakannya sebagai 'C yang lebih baik' dan menggunakan Rcpp sebagai perekat ke R tidak masalah. Tidak ada yang memaksakan gaya pada Anda - ini bukan Java ;-)
Dirk Eddelbuettel
@Dirk: thx for the elaboration. Ini menimbulkan pertanyaan di kantor kami sebelumnya, karena C biasanya digunakan di sini, bukan C ++. Kapan penggunaan C di atas C ++ bermanfaat, atau apakah Anda hanya mengatakan "tidak pernah C, selalu C ++"?
Joris Meys
Hadley: Keren. Kami akan sangat tertarik dengan tanggapan Anda. Silakan bergabung dengan rcpp-devel dan jangan ditahan. Kami tahu bahwa kami adalah dokumentasi singkat - tetapi pandangan baru dapat sangat membantu.
Dirk Eddelbuettel
6
@adley apakah itu berarti bahwa kita dapat mengharapkan beberapa peningkatan kecepatan ggplot?
aL3xa
56

Hadley,

Anda pasti bisa menulis kode C ++ yang mirip dengan kode C.

Saya mengerti apa yang Anda katakan tentang C ++ lebih rumit daripada C. Ini adalah jika Anda ingin menguasai semuanya: objek, templat, STL, pemrograman meta templat, dll ... kebanyakan orang tidak membutuhkan hal-hal ini dan hanya dapat mengandalkan orang lain untuk itu. Penerapan Rcpp sangat rumit, tetapi hanya karena Anda tidak tahu cara kerja lemari es Anda, itu tidak berarti Anda tidak dapat membuka pintu dan mengambil susu segar ...

Dari banyak kontribusi Anda ke R, yang mengejutkan saya adalah Anda menganggap R agak membosankan (manipulasi data, grafik, manipulasi string, dll ...). Bersiaplah untuk lebih banyak kejutan dengan API C internal R. Ini sangat membosankan.

Dari waktu ke waktu, saya membaca manual R-exts atau R-ints. Ini membantu. Tetapi sebagian besar waktu, ketika saya benar-benar ingin mencari tahu tentang sesuatu, saya pergi ke sumber R, dan juga di sumber paket yang ditulis oleh misalnya Simon (biasanya ada banyak hal yang harus dipelajari di sana).

Rcpp dirancang untuk menghilangkan aspek-aspek API yang membosankan ini.

Anda dapat menilai sendiri apa yang menurut Anda lebih rumit, tidak jelas, dll ... berdasarkan beberapa contoh. Fungsi ini membuat vektor karakter menggunakan 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);
}

Menggunakan Rcpp, Anda dapat menulis fungsi yang sama dengan:

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

atau:

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

Seperti yang dikatakan Dirk, ada contoh lain pada beberapa sketsa. Kami juga biasanya mengarahkan orang ke pengujian unit kami karena masing-masing menguji bagian yang sangat spesifik dari kode dan cukup jelas.

Saya jelas bias di sini, tetapi saya akan merekomendasikan untuk mengenal Rcpp daripada mempelajari C API dari R, dan kemudian datang ke milis jika ada sesuatu yang tidak jelas atau tampaknya tidak dapat dilakukan dengan Rcpp.

Bagaimanapun, akhir dari promosi dagang.

Saya kira itu semua tergantung pada jenis kode yang ingin Anda tulis pada akhirnya.

Romain

Romain Francois
sumber
2
"Rcpp dirancang untuk menghilangkan aspek-aspek API yang membosankan ini" = persis seperti yang saya cari. Terima kasih! Apa yang akan sangat berguna adalah primer C ++ v. Brief untuk seseorang yang akrab dengan C dan ingin menggunakan Rcpp.
hadley
bagus, contoh singkat Rcpp itu membuat saya terjual. Saya mengasumsikan alokasiXX dan UNPROTECT (1) ditangani seperti cara smart pointer mengelola sumber daya. yaitu RAII. Apakah ada penalti kinerja yang menonjol dengan menggunakan Rcpp di atas api vanilla C?
jbremnant
Kami mengatasinya dalam pengantar Rcpp dengan contoh benchmark (yang juga ada dalam paket sources / terinstal). Singkatnya, tidak ada penalti sama sekali.
Dirk Eddelbuettel
29

@adley: sayangnya, saya tidak memiliki sumber daya khusus untuk membantu Anda memulai C ++. Saya mengambilnya dari buku Scott Meyers (Efektif C ++, C ++ Lebih efektif, dll ...) tetapi ini tidak benar-benar apa yang bisa disebut pengantar.

Kami hampir secara eksklusif menggunakan antarmuka .Call untuk memanggil kode C ++. Aturannya cukup mudah:

  • Fungsi C ++ harus mengembalikan objek R. Semua objek R adalah SEXP.
  • Fungsi C ++ mengambil antara 0 dan 65 R objek sebagai input (lagi-lagi SEXP)
  • harus (tidak benar-benar, tapi kita bisa menyimpan ini untuk nanti) dinyatakan dengan C linkage, baik dengan extern "C" atau RcppExport alias yang mendefinisikan Rcpp.

Jadi fungsi .Call dideklarasikan seperti ini di beberapa file header:

#include <Rcpp.h>

RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;

dan diimplementasikan seperti ini dalam file .cpp:

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

Tidak banyak lagi yang perlu diketahui tentang R API untuk menggunakan Rcpp.

Kebanyakan orang hanya ingin berurusan dengan vektor numerik di Rcpp. Anda melakukan ini dengan kelas NumericVector. Ada beberapa cara untuk membuat vektor numerik:

Dari objek yang ada yang Anda turunkan dari R:

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

Dengan nilai yang diberikan menggunakan fungsi statis :: create:

 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
 ) ;

Dari ukuran tertentu:

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

Kemudian setelah Anda memiliki vektor, hal yang paling berguna adalah mengekstrak satu elemen darinya. Ini dilakukan dengan operator [], dengan pengindeksan berbasis 0, jadi misalnya menjumlahkan nilai vektor numerik berjalan seperti ini:

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 ) ;
}

Tetapi dengan gula Rcpp kita dapat melakukan ini dengan lebih baik sekarang:

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

Seperti yang saya katakan sebelumnya, itu semua tergantung pada jenis kode yang ingin Anda tulis. Lihatlah apa yang dilakukan orang-orang dalam paket yang mengandalkan Rcpp, periksa sketsa, tes unit, kembali kepada kami di milis. Kami selalu senang membantu.

Romain Francois
sumber
20

@jbremnant: Benar. Kelas rcpp mengimplementasikan sesuatu yang dekat dengan pola RAII. Saat objek Rcpp dibuat, konstruktor mengambil tindakan yang tepat untuk memastikan objek R yang mendasari (SEXP) dilindungi dari pengumpul sampah. Perusak menarik perlindungan. Ini dijelaskan dalam sketsa pengantar-Rcpp . Implementasi yang mendasari bergantung pada fungsi R API R_PreserveObject dan R_ReleaseObject

Memang ada penalti kinerja karena enkapsulasi C ++. Kami mencoba untuk menjaga ini seminimal mungkin dengan inlining, dll ... Hukumannya kecil, dan ketika Anda memperhitungkan keuntungan dalam hal waktu yang diperlukan untuk menulis dan memelihara kode, itu tidak terlalu relevan.

Memanggil fungsi R dari fungsi kelas Rcpp lebih lambat daripada langsung memanggil eval dengan api C. Ini karena kami melakukan tindakan pencegahan dan menggabungkan panggilan fungsi ke dalam blok tryCatch sehingga kami menangkap kesalahan R dan mempromosikannya ke pengecualian C ++ sehingga dapat ditangani menggunakan coba / tangkap standar di C ++.

Kebanyakan orang ingin menggunakan vektor (khususnya NumericVector), dan hukumannya sangat kecil dengan kelas ini. Direktori example / ConvolveBenchmarks berisi beberapa varian fungsi konvolusi terkenal dari R-exts dan sketsa memiliki hasil benchmark. Ternyata Rcpp membuatnya lebih cepat dari kode benchmark yang menggunakan R API.

Romain Francois
sumber