Gdzie mogę nauczyć się pisać kod C, aby przyspieszyć powolne funkcje R? [zamknięte]

Jaki jest najlepszy zasób do nauki pisania kodu C do użycia z R? Wiem o Sekcji systemu i interfejsów obcojęzycznych rozszerzeń R, ale uważam, że jest to dość trudne. Jakie są dobre zasoby (zarówno online, jak i offline) do pisania kodu C do użytku z R?

Aby wyjaśnić, nie chcę nauczyć się pisać kodu C, chcę dowiedzieć się, jak lepiej zintegrować R i C. na przykład, jak przekonwertować z wektora C do wektora R (lub vice versa) czy ze skalaru C na wektor R?

 103
Author: Jaap, 2010-11-05

4 answers

Cóż, jest stare dobre Użyj źródła, Luke! - - - sam R ma mnóstwo (bardzo wydajnego) kodu C, który można studiować, a CRAN ma setki pakietów, niektóre Od autorów, którym ufasz. To daje prawdziwe, sprawdzone przykłady do zbadania i dostosowania.

Ale jak podejrzewał Josh, bardziej skłaniam się ku C++ , a co za tym idzie Rcpp. Ma również wiele przykładów.

Edit: były dwie książki, które uznałam za pomocne:

  • pierwszy to Venables i Ripley ' s "S Programowanie " mimo, że robi się długo w zębie (a od lat krążą plotki o 2 edycji). W tym czasie nie było po prostu nic innego.
  • drugi w "Software for Data Analysis" Chambersa, który jest znacznie nowszy i ma znacznie ładniejszy r-centric feel -- I dwa rozdziały na temat rozszerzania R. zarówno C jak i C++ są wymienione. Poza tym, John rozwala mnie za to, co zrobiłem zdigest , więc samo jest warte ceny wstęp.

To powiedziawszy, John coraz bardziej lubi Rcpp (i przyczynia się do tego), ponieważ uważa, że dopasowanie między obiektami R i obiektami C++ (poprzez Rcpp ) jest bardzo naturalne-i tam pomagają Referenceclases.

Edit 2: z pytaniem Hadleya, bardzo mocno zachęcam do rozważenia C++. Jest tyle bzdur o kotle, które masz do czynienia z C - - - bardzo nudne i bardzo możliwe do uniknięcia . Zajrzyj do rcpp-wprowadzenie winieta . Innym prostym przykładem jest ten post na blogu , w którym pokazuję, że zamiast martwić się o różnice 10% (w jednym z przykładów Radforda Neala) możemy uzyskać ośmiokrotny wzrost z C++ (na tym, co jest oczywiście wymyślonym przykładem).

Edit 3: istnieje złożoność w tym, że możesz napotkać błędy C++, które są, delikatnie mówiąc, trudne do wygładzenia. Ale aby po prostu używać Rcpp zamiast go rozszerzać, prawie nigdy nie powinieneś go potrzebować. I podczas gdy to Koszt {[2] } jest niezaprzeczalny, jest znacznie przyćmiony przez korzyść prostszego kodu, mniej boilerplate, no PROTECT/UNPROTECT, no memory management etc pp. Doug Bates właśnie wczoraj stwierdził, że uważa, że C++ i Rcpp są bardziej podobne do pisania R niż pisania C++. YMMV i tak dalej.

 66
Author: Dirk Eddelbuettel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-05 14:12:36

Hadley,

Zdecydowanie możesz napisać kod C++, który jest podobny do kodu C.

Rozumiem, co mówisz o C++ bardziej skomplikowanym niż C. Jest tak, jeśli chcesz opanować wszystko : obiekty, szablony, stl, programowanie szablonów itp ... większość ludzi nie potrzebuje tych rzeczy i może po prostu polegać na innych. Wdrożenie Rcpp jest bardzo skomplikowane, ale to, że nie wiesz, jak działa Twoja lodówka, nie oznacza, że nie możesz otworzyć drzwi i chwycić świeże mleko ...

Z Twoich licznych wkładów do R, uderza mnie to, że uważasz R za nieco nużący (manipulacja danymi, grafika, manipulacja łańcuchami itp ...). Przygotuj się na wiele innych niespodzianek z wewnętrznym C API R. Jest to bardzo żmudne.

Od czasu do czasu czytam podręczniki r-exts lub R-ints. To pomaga. Ale najczęściej, kiedy naprawdę chcę się o czymś dowiedzieć, wchodzę do źródła R, a także do źródła pakietów napisanych np. przez Simona (zwykle jest tam wiele do nauczenia).

Rcpp ma na celu wyeliminowanie tych żmudnych aspektów API.

Możesz sam ocenić, co uważasz za bardziej skomplikowane, zaciemnione itp ... na podstawie kilku przykładów. Funkcja ta tworzy wektor znaków za pomocą interfejsu API C:

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

Używając Rcpp, możesz napisać taką samą funkcję jak:

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

Lub:

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

Jak powiedział Dirk, są inne przykłady na kilku winietach. Zwykle też wskazujemy ludzi w kierunku naszych testów jednostkowych, ponieważ każdy z nich testuje bardzo specyficzną część kodu i są nieco oczywiste.

Jestem oczywiście stronniczy tutaj, ale zalecałbym zapoznanie się z Rcpp zamiast uczenia się C API R, a następnie przyjść na listę dyskusyjną, jeśli coś jest niejasne lub nie wydaje się wykonalne z Rcpp.

Koniec sprzedaży.

Myślę, że wszystko zależy od tego, jaki kod chcesz w końcu napisać.

Romain

 52
Author: Romain Francois,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-05 14:47:20

@hadley: niestety, nie mam na myśli konkretnych zasobów, które pomogą Ci zacząć pracę z C++. Podniosłem go z książek Scotta Meyersa (efektywny C++, bardziej efektywny C++ itp ...), ale nie są to tak naprawdę rzeczy, które można nazwać wprowadzającymi.

[[7]}używamy prawie wyłącznie .Interfejs wywołujący wywołanie kodu C++. Zasada jest dość prosta:
  • funkcja C++ musi zwracać obiekt R. Wszystkie obiekty R są SEXP.
  • funkcja C++ zajmuje od 0 do 65 R obiekty jako wejście (ponownie SEXP)
  • musi (nie do końca, ale możemy to zapisać na później) być zadeklarowane za pomocą połączenia C, albo za pomocą extern "C" lub rcppexport Alias zdefiniowany przez Rcpp.

Więc a .Funkcja wywołania jest deklarowana w ten sposób w jakimś pliku nagłówkowym:

#include <Rcpp.h>

RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;

I zaimplementowane w ten sposób wplik cpp:

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

Nie ma zbyt wiele informacji o r API, aby używać Rcpp.

Większość ludzi chce zajmować się tylko numerycznymi wektory w Rcpp. Robisz to z klasą NumericVector. Istnieje kilka sposobów tworzenia wektora liczbowego:

From an existing object that you pass down from R:

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

Z podanymi wartościami za pomocą:: create static function:

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

O podanym rozmiarze:

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

Wtedy, gdy masz wektor, najbardziej przydatną rzeczą jest wyodrębnienie z niego jednego elementu. Odbywa się to za pomocą operatora [], z indeksowaniem opartym na 0, więc na przykład sumowanie wartości wektora liczbowego idzie coś takiego:

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

Ale z cukrem Rcpp możemy teraz zrobić to znacznie przyjemniej:

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

Jak już mówiłem, wszystko zależy od tego, jaki kod chcesz napisać. Sprawdź, co ludzie robią w pakietach, które bazują na Rcpp, sprawdź winiety, testy jednostkowe, wróć do nas na listę mailingową. Zawsze chętnie pomożemy.

 28
Author: Romain Francois,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-08 12:32:46

@jbremnant: zgadza się. Klasy Rcpp implementują coś zbliżonego do wzorca RAII. Kiedy tworzony jest obiekt Rcpp, konstruktor podejmuje odpowiednie środki w celu zapewnienia ochrony bazowego obiektu R (SEXP) przed garbage collector. Destruktor wycofuje ochronę. Jest to wyjaśnione w winiecie rcpp-intruction. Implementacja bazowa opiera się na funkcjach R API r_preserveobject i R_ReleaseObject

Jest rzeczywiście kary za wydajność z powodu enkapsulacji C++. Staramy się utrzymać to na minimalnym poziomie dzięki inliningowi itp ... Kara jest niewielka, a biorąc pod uwagę zysk w postaci czasu potrzebnego na napisanie i utrzymanie kodu, nie jest to istotne.

Wywołanie funkcji r z funkcji klasy rcpp jest wolniejsze niż bezpośrednie wywołanie eval z API C. Dzieje się tak dlatego, że podejmujemy środki ostrożności i zawijamy wywołanie funkcji w blok tryCatch tak, że wychwytywamy błędy R i promujemy je do C++ wyjątki, dzięki którym można sobie z nimi poradzić używając standardowego try/catch w C++.

Większość ludzi chce używać wektorów (szczególnie NumericVector), a kara jest bardzo mała w przypadku tej klasy. Katalog examples / ConvolveBenchmarks zawiera kilka wariantów znanej funkcji splotowej z R-exts, a winieta ma wyniki porównawcze. Okazuje się, że Rcpp czyni go szybszym niż kod benchmark, który korzysta z API R.

 19
Author: Romain Francois,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-08 12:12:18