느린 R 함수의 속도를 높이기 위해 C 코드를 작성하는 방법을 어디서 배울 수 있습니까? [닫은]


115

R과 함께 사용할 C 코드를 작성하는 방법을 배우는 데 가장 좋은 리소스는 무엇입니까? R 확장 의 시스템 및 외국어 인터페이스 섹션에 대해 알고 있지만 꽤 어렵습니다. R과 함께 사용할 C 코드를 작성하는 데 유용한 리소스 (온라인 및 오프라인 모두)는 무엇입니까?

명확히하기 위해 C 코드를 작성하는 방법을 배우고 싶지 않고 R과 C를 더 잘 통합하는 방법을 배우고 싶습니다. 예를 들어 C 정수 벡터에서 R 정수 벡터로 (또는 그 반대로) 변환하는 방법 또는 C 스칼라에서 R 벡터로?

답변:


71

음, 좋은 오래된 것이 있습니다 소스를 사용하십시오, 루크! --- R 자체에는 연구 할 수있는 많은 (매우 효율적인) C 코드가 있으며 CRAN에는 수백 개의 패키지가 있으며 일부는 신뢰할 수있는 작성자가 제공합니다. 이는 연구하고 적응할 수있는 실제 테스트 된 예제를 제공합니다.

그러나 Josh가 의심 했듯이 나는 C ++에 더 의존 하므로 Rcpp . 또한 많은 예가 있습니다.

편집 : 도움이 된 두 권의 책이 있습니다.

  • 첫 번째는 Venables와 Ripley의 " S Programming "입니다. 비록 이빨이 길어지고 있지만 몇 년 동안 2 판에 대한 소문이있었습니다. 당시에는 다른 것이 없었습니다.
  • Chambers의 " 데이터 분석을위한 소프트웨어 "의 두 번째 는 훨씬 더 최근이고 훨씬 더 좋은 R 중심적 느낌을 가지고 있으며 R 확장에 대한 두 개의 장이 있습니다. C와 C ++ 모두 언급됩니다. 또한 John은 내가 다이제스트 로 한 일에 대해 나를 파쇄 하여 혼자서 입장료의 가치가 있습니다.

즉, John은 R 개체와 C ++ 개체 ( Rcpp 를 통해 ) 간의 일치 가 매우 자연 스럽다는 것을 알게되면서 Rcpp를 좋아 하고 기여 하고 있습니다. 그리고 ReferenceClasses가 도움이됩니다.

편집 2 : Hadley의 다시 초점을 맞춘 질문 으로 C ++를 고려할 것을 강력히 촉구합니다. C --- 매우 지루하고 매우 피할 수있는 상용구 말도 안되는 일이 너무 많습니다 . Rcpp 소개 비 네트를 살펴보십시오 . 또 다른 간단한 예는 10 % 차이에 대해 걱정하는 대신 (Radford Neal 예제 중 하나에서) C ++ (물론 인위적인 예제에서)로 80 배 증가 할 수 있음을 보여주는 이 블로그 게시물 입니다.

편집 3 : C ++ 오류가 발생할 수 있다는 점에서 약간 복잡합니다. 그러나 Rcpp 를 확장하는 대신 사용하려면 거의 필요하지 않습니다. 그리고이 비용 은 부인할 수 없지만 더 간단한 코드, 더 적은 상용구, 보호 / 비보호, 메모리 관리 등 의 이점 으로 인해 훨씬 ​​가려집니다 . C ++를 작성하는 것보다. YMMV 및 모든 것.


나는 "use Rcpp"답을 얻을 것이라고 예상했다;) C 대신 C ++를 사용할 때의 단점을 설명 할 수 있다면 정말 유용 할 것이다. 한 가지 중요한 것은 C ++가 C보다 훨씬 더 복잡하다는 것입니다. 이로 인해 사용하기가 더 어렵습니까? (또는 실제로 C와 매우 유사한 C ++ 코드를 작성할 수 있습니까?) 기존 C API에 익숙하지 않은 신규 사용자를 대상으로하는 더 많은 참조 자료도 감사하겠습니다.
hadley

2
편집 3을 참조 하고 예, 할 수 있습니다 . Meyers는 C ++를 '네 가지 패러다임'언어라고 부르며 네 가지를 모두 사용할 필요는 없습니다. '더 나은 C'로 사용하고 Rcpp를 R에 대한 접착제로 사용하는 것은 완벽합니다. 아무도 당신의 스타일을 강제하지 -이 ;-) 자바 아니다
더크 Eddelbuettel

@Dirk : 정교함을 위해 thx. 여기에서는 C ++ 대신 C가 일반적으로 사용되기 때문에 이전에 우리 사무실에서 의문을 제기했습니다. C ++보다 C를 사용하는 것이 언제 유익할까요? 아니면 단순히 "C는 절대로, 항상 C ++"라고 말합니까?
Joris Meys

Hadley : 좋아요. 귀하의 의견에 매우 관심이 있습니다. 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 ;
}

Dirk가 말했듯이 여러 비 네트에 대한 다른 예가 있습니다. 우리는 또한 사람들이 각각 코드의 매우 특정한 부분을 테스트하고 다소 자명하기 때문에 단위 테스트로 사람들을 안내합니다.

나는 분명히 여기에 편향되어 있지만 R의 C API를 배우는 대신 Rcpp에 대해 익숙해지는 것이 좋습니다. 그리고 무언가가 명확하지 않거나 Rcpp로 할 수없는 것 같으면 메일 링리스트로 오십시오.

어쨌든 판매 피치 끝.

결국에는 어떤 종류의 코드를 작성 하느냐에 달려 있다고 생각합니다.

Romain


2
"Rcpp는 API의 이러한 지루한 측면을 없애도록 설계되었습니다."= 정확히 제가 찾고있는 것입니다. 감사! 정말 유용한 것은 C에 익숙하고 Rcpp를 사용하려는 사람을위한 v. 간단한 C ++ 입문서 일 것입니다.
hadley

좋아, Rcpp의 짧은 예가 나를 팔게 만들었다. 나는 allocXX 및 UNPROTECT (1)가 스마트 포인터가 리소스를 관리하는 방식과 매우 유사하다고 가정하고 있습니다. 즉, RAII. 바닐라 C API보다 Rcpp를 사용하면 눈에 띄는 성능 저하가 있습니까?
jbremnant 2010

Rcpp 소개에서 벤치 마크 예제 (소스 / 설치된 패키지에도 있음)를 사용하여이를 해결합니다. 간단히 말해, 벌금이 전혀 없습니다.
Dirk Eddelbuettel 2010

29

@hadley : 안타깝게도 C ++를 시작하는 데 도움이되는 특정 리소스를 염두에 두지 않았습니다. 나는 Scott Meyers의 책 (Effective C ++, More effective C ++, etc ...)에서이 책을 집어 들었지만 실제로 입문이라고 할 수있는 것은 아닙니다.

우리는 거의 독점적으로 .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

그런 다음 벡터가 있으면 가장 유용한 것은 벡터에서 하나의 요소를 추출하는 것입니다. 이것은 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_PreserveObjectR_ReleaseObject 에 의존합니다.

실제로 C ++ 캡슐화로 인해 성능이 저하됩니다. 우리는 인라인 등으로 이것을 최소한으로 유지하려고 노력합니다. 패널티는 적으며 코드를 작성하고 유지하는 데 걸리는 시간 측면에서 이득을 고려할 때 그다지 적절하지 않습니다.

Rcpp 클래스 Function에서 R 함수를 호출하는 것은 C API를 사용하여 eval을 직접 호출하는 것보다 느립니다. 이는 예방 조치를 취하고 함수 호출을 tryCatch 블록으로 래핑하여 R 오류를 캡처하고 C ++에서 표준 try / catch를 사용하여 처리 할 수 ​​있도록 C ++ 예외로 승격하기 때문입니다.

대부분의 사람들은 벡터 (특히 NumericVector)를 사용하기를 원하며이 클래스의 패널티는 매우 적습니다. examples / ConvolveBenchmarks 디렉토리에는 R-exts의 악명 높은 convolution 함수의 여러 변형이 포함되어 있으며 비 네트에는 벤치 마크 결과가 있습니다. Rcpp는 R API를 사용하는 벤치 마크 코드보다 더 빠릅니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.