Yavaş R işlevlerini hızlandırmak için C kodu yazmayı nereden öğrenebilirim? [kapalı]


115

R ile kullanılmak üzere C kodu yazmayı öğrenmek için en iyi kaynak nedir? R uzantılarının sistem ve yabancı dil arayüzleri bölümünü biliyorum , ancak bunu oldukça zor buluyorum. R ile kullanılmak üzere C kodu yazmak için iyi kaynaklar (hem çevrimiçi hem çevrimdışı) nelerdir?

Açıklığa kavuşturmak için, C kodu yazmayı öğrenmek istemiyorum, R ve C'yi nasıl daha iyi entegre edeceğimi öğrenmek istiyorum.Örneğin, bir C tamsayı vektöründen bir R tamsayı vektörüne (veya tam tersi) nasıl dönüştürebilirim? veya bir C skalasından R vektörüne?

Yanıtlar:


71

Eh, eski güzel kaynağı kullan, Luke! --- R'nin kendisi üzerinde çalışabileceğiniz çok sayıda (çok verimli) C kodu vardır ve CRAN'ın bazıları güvendiğiniz yazarlardan gelen yüzlerce paketi vardır. Bu, çalışmak ve uyarlamak için gerçek, test edilmiş örnekler sağlar.

Ancak Josh'un tahmin ettiği gibi, C ++ 'ya ve dolayısıyla Rcpp'ye daha çok eğiliyorum . Ayrıca birçok örneği var.

Düzenleme: Yararlı bulduğum iki kitap vardı:

  • İlki, Venables ve Ripley'nin " S Programlaması " derken dişte uzun sürüyor (ve yıllardır 2. baskının söylentileri var). O zamanlar başka hiçbir şey yoktu.
  • Chambers'ın çok daha yeni olan ve çok daha hoş bir R-merkezli hissi olan " Veri Analizi Yazılımı " ndaki ikincisi ve R'nin genişletilmesi üzerine iki bölümden bahsedilir. Hem C hem de C ++ 'dan bahsedilir. Ayrıca John, sindirimle yaptığım şey için beni parçalara ayırıyor, böylece tek başına giriş ücretine değer.

Bununla birlikte, John, R nesneleri ve C ++ nesneleri ( Rcpp aracılığıyla ) arasındaki eşleşmeyi çok doğal bulduğu için Rcpp'ye düşkün (ve katkıda bulunarak) ve ReferenceClasses orada yardımcı oluyor.

Düzenleme 2: Hadley'in tekrarlanan sorusuyla, C ++ 'yı düşünmenizi şiddetle tavsiye ediyorum. C ile yapmak zorunda olduğunuz çok fazla standart saçmalık var - çok sıkıcı ve çok önlenebilir . Rcpp giriş vinyetine bir göz atın . Bir başka basit örnek ise, yaklaşık% 10'luk farklılıklardan endişelenmek yerine (Radford Neal örneklerinden birinde) C ++ ile seksen kat artışlar elde edebileceğimizi gösterdiğim bu blog yazısıdır (elbette uydurulmuş bir örnek olarak).

Düzenleme 3: Hafifçe söylemek gerekirse, sarkması zor olan C ++ hatalarıyla karşılaşabileceğiniz karmaşıklık var. Ancak onu genişletmek yerine sadece Rcpp kullanmak için ona neredeyse hiç ihtiyacınız olmaz. Bu ederken maliyet yadsınamaz, bu kadar yoğun gölgesi altında yararına daha basit kod, daha az klişe, hiçbir korunması / korumanın, hiçbir bellek yönetimi vb ss. Doug Bates daha dün C bulur ++ ve Rcpp çok daha R yazma gibi olmak olduğunu belirtti C ++ yazmaktan çok. YMMV ve hepsi.


Bir "Rcpp kullan" cevabı alacağımı umuyordum;) C yerine C ++ kullanmanın dezavantajlarını açıklayabilirseniz gerçekten yararlı olacaktır. Önemli olanlardan biri, C ++ 'nın C'den çok daha karmaşık olması gibi görünüyor bu kullanımı zorlaştırıyor mu? (Veya pratikte, C'ye çok benzeyen C ++ kodu yazabilir misiniz?) Mevcut C api'ye aşina olmayan yeni kullanıcıları hedefleyen daha fazla referans materyali de takdir ediyorum.
hadley

2
Düzenleme 3'e bakın ve evet, yapabilirsiniz . Meyers, C ++ 'yı' dört paradigma 'dili olarak adlandırır ve dördünü de kullanmak zorunda değilsiniz. Bunu 'sadece daha iyi bir C' olarak kullanmak ve Rcpp'yi R'ye yapıştırıcı olarak kullanmak tamamen iyidir. Kimse size bir stil
zorlamıyor

@Dirk: detaylandırma için teşekkürler. Burada C ++ yerine C yaygın olarak kullanıldığı için daha önce ofisimizde de soruyu gündeme getirdi. C ++ yerine C'nin kullanılması ne zaman yararlı olur, yoksa sadece "asla C, her zaman C ++" mı diyorsunuz?
Joris Meys

Hadley: Harika. Geri bildiriminizle çok ilgileneceğiz. Lütfen rcpp-devel'e katılın ve geri durmayın. Kısa dokümantasyon olduğumuzu biliyoruz - ancak yeni bir bakış açısı muazzam bir şekilde yardımcı olabilir.
Dirk Eddelbuettel

6
@hadley bu, bazı hız iyileştirmeleri bekleyebileceğimiz anlamına ggplotmı geliyor?
aL3xa

56

Hadley,

Kesinlikle C koduna benzer C ++ kodu yazabilirsiniz.

C ++ hakkında söylediklerinin C'den daha karmaşık olduğunu anlıyorum.Bu, her şeye hakim olmak istiyorsan: nesneler, şablonlar, STL, şablon meta programlama vb. Çoğu insan bunlara ihtiyaç duymaz ve sadece başkalarına güvenebilir ona. Rcpp'nin uygulanması çok karmaşık, ancak buzdolabınızın nasıl çalıştığını bilmediğiniz için, kapıyı açıp taze süt alamayacağınız anlamına gelmez ...

R'ye yaptığınız birçok katkıdan, beni etkileyen şey, R'yi biraz sıkıcı bulmanızdır (veri işleme, grafikler, dizgi işleme, vb.). R'nin dahili C API'si ile daha birçok sürpriz için hazırlanın. Bu çok sıkıcı.

Zaman zaman R-exts veya R-ints kılavuzlarını okurum. Bu yardımcı olur. Ama çoğu zaman, bir şeyi gerçekten öğrenmek istediğimde, R kaynağına ve ayrıca örneğin Simon tarafından yazılan paketlerin kaynağına giderim (genellikle orada öğrenecek çok şey vardır).

Rcpp, API'nin bu sıkıcı yönlerini ortadan kaldırmak için tasarlanmıştır.

Daha karmaşık, şaşkın vb. Bulduğunuz şeyleri birkaç örneğe dayanarak kendiniz yargılayabilirsiniz. Bu işlev, C API'yi kullanarak bir karakter vektörü oluşturur:

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 kullanarak, aynı işlevi şu şekilde yazabilirsiniz:

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

veya:

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

Dirk'in dediği gibi, birkaç kısa hikayede başka örnekler de var. Ayrıca insanları genellikle birim testlerimize yönlendiririz çünkü her biri kodun çok özel bir bölümünü test eder ve bir şekilde kendinden açıklamalıdır.

Açıkçası burada önyargılıyım, ancak R'nin C API'sini öğrenmek yerine Rcpp hakkında bilgi edinmenizi ve ardından bir şey net değilse veya Rcpp ile mümkün görünmüyorsa posta listesine gelmenizi öneririm.

Her neyse, satış konuşmasının sonu.

Sanırım hepsi sonunda ne tür bir kod yazmak istediğinize bağlı.

Romain


2
"Rcpp, API'nin bu sıkıcı yönlerini ortadan kaldırmak için tasarlandı" = tam olarak aradığım şey. Teşekkürler! Gerçekten yararlı olan, C'ye aşina olan ve Rcpp'yi kullanmak isteyen biri için v. Kısa bir C ++ astarı olacaktır.
hadley

güzel, bu kısa Rcpp örneği beni sattı. TahsisXX ve UNPROTECT (1) 'in, akıllı işaretçilerin kaynağı nasıl yönettiği gibi ele alındığını varsayıyorum. yani RAII. Vanilya C api yerine Rcpp kullanarak kayda değer bir performans cezası var mı?
jbremnant

Bunu Rcpp girişinde bir kıyaslama örneğiyle (aynı zamanda kaynaklarda / kurulu pakette bulunan) ele alıyoruz. Kısacası hiçbir ceza yok.
Dirk Eddelbuettel

29

@hadley: Maalesef, C ++ kullanmaya başlamanıza yardımcı olacak belirli kaynaklarım yok. Bunu Scott Meyers'in kitaplarından aldım (Etkili C ++, Daha Etkili C ++, vb.) Ama bunlar gerçekten giriş olarak adlandırılabilecek şeyler değil.

C ++ kodunu çağırmak için neredeyse yalnızca .Call arayüzünü kullanıyoruz. Kural yeterince kolaydır:

  • C ++ işlevi bir R nesnesi döndürmelidir. Tüm R nesneleri SEXP'dir.
  • C ++ işlevi girdi olarak 0 ila 65 R nesnesi alır (yine SEXP)
  • (gerçekten değil, ama bunu daha sonrası için kaydedebiliriz) ya extern "C" veya Rcpp'nin tanımladığı RcppExport takma adı ile C bağlantısı ile bildirilmelidir .

Yani bir .Call işlevi bazı başlık dosyasında şu şekilde bildirilir:

#include <Rcpp.h>

RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;

ve bir .cpp dosyasında şu şekilde uygulandı:

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

Rcpp kullanmak için R API hakkında bilinmesi gereken çok şey yok.

Çoğu insan sadece Rcpp'de sayısal vektörlerle uğraşmak ister. Bunu NumericVector sınıfıyla yaparsınız. Sayısal bir vektör oluşturmanın birkaç yolu vardır:

R'den aktardığınız mevcut bir nesneden:

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

:: create static işlevini kullanarak verilen değerlerle:

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

Belirli bir boyutta:

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

Sonra bir vektöre sahip olduğunuzda, en kullanışlı şey ondan bir element çıkarmaktır. Bu, 0 tabanlı indekslemeyle [] operatörüyle yapılır, bu nedenle örneğin bir sayısal vektörün değerlerinin toplanması şu şekilde olur:

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

Ancak Rcpp şeker ile bunu şimdi çok daha güzel bir şekilde yapabiliriz:

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

Daha önce de söylediğim gibi, her şey ne tür bir kod yazmak istediğinize bağlı. İnsanların Rcpp'ye dayanan paketlerde ne yaptığına bakın, vinyetleri kontrol edin, birim testleri, posta listesinde bize geri dönün. Yardımcı olmaktan her zaman mutluluk duyarız.


20

@jbremnant: Doğru. Rcpp sınıfları RAII modeline yakın bir şey uygular. Bir Rcpp nesnesi oluşturulduğunda, yapıcı, temeldeki R nesnesinin (SEXP) çöp toplayıcıdan korunmasını sağlamak için uygun önlemleri alır. Yıkıcı, korumayı geri çeker. Bu, Rcpp-saldırı vinyetinde açıklanmıştır . Temel uygulama, R API işlevleri R_PreserveObject ve R_ReleaseObject'e dayanır.

C ++ kapsülleme nedeniyle gerçekten de performans cezası var. Bunu satır içi vb. İle minimumda tutmaya çalışıyoruz ... Ceza küçüktür ve kodu yazmak ve sürdürmek için gereken süre açısından kazancı hesaba katarsanız, bu pek alakalı değildir.

Rcpp sınıfından R işlevlerini çağırma İşlev, C api ile doğrudan eval çağırmaktan daha yavaştır. Bunun nedeni, önlem almamız ve işlev çağrısını bir tryCatch bloğuna sarmamızdır, böylece R hatalarını yakalarız ve bunları C ++ istisnalarına yükseltiriz, böylece bunlar C ++ 'da standart dene / yakala kullanılarak ele alınabilirler.

Çoğu insan vektörleri (özellikle NumericVector) kullanmak ister ve bu sınıfta ceza çok azdır. Örnekler / ConvolveBenchmarks dizini, R-exts'ten kötü şöhretli evrişim işlevinin çeşitli varyantlarını içerir ve vinyet, kıyaslama sonuçlarına sahiptir. Rcpp'nin, R API'yi kullanan kıyaslama kodundan daha hızlı hale getirdiği ortaya çıktı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.