ฉันจะเรียนรู้วิธีเขียนโค้ด C เพื่อเร่งความเร็วฟังก์ชัน R ช้าได้ที่ไหน [ปิด]


115

แหล่งข้อมูลที่ดีที่สุดสำหรับการเรียนรู้วิธีเขียนโค้ด C เพื่อใช้กับ R คืออะไร ฉันรู้เกี่ยวกับระบบและส่วนต่อประสานภาษาต่างประเทศของส่วนขยาย R แต่ฉันพบว่ามันค่อนข้างยาก แหล่งข้อมูลที่ดี (ทั้งออนไลน์และออฟไลน์) สำหรับการเขียนโค้ด C เพื่อใช้กับ R คืออะไร?

เพื่อชี้แจงฉันไม่ต้องการเรียนรู้วิธีการเขียนโค้ด C ฉันต้องการเรียนรู้วิธีรวม R และ C ให้ดีขึ้นตัวอย่างเช่นฉันจะแปลงจากเวกเตอร์จำนวนเต็ม C เป็นเวกเตอร์จำนวนเต็ม R ได้อย่างไร (หรือในทางกลับกัน) หรือจากสเกลาร์ C เป็นเวกเตอร์ R?

คำตอบ:


71

มีความเก่าที่ดีใช้แหล่งที่มาลุค! --- R เองก็มีรหัส C (ที่มีประสิทธิภาพสูง) มากมายที่สามารถศึกษาได้และ CRAN มีแพ็คเกจหลายร้อยชุดซึ่งบางส่วนมาจากผู้เขียนที่คุณเชื่อถือ ที่ให้ตัวอย่างจริงที่ผ่านการทดสอบเพื่อศึกษาและปรับใช้

แต่เป็นที่น่าสงสัยว่าจอชผมยันขึ้นต่อ C ++ และด้วยเหตุนี้Rcpp นอกจากนี้ยังมีตัวอย่างมากมาย

แก้ไข:มีหนังสือสองเล่มที่ฉันพบว่ามีประโยชน์:

  • อันแรกคือ Venables และ " S Programming " ของ Ripley แม้ว่าจะอยู่ในช่วงฟันฝ่ามานาน (และมีข่าวลือเกี่ยวกับฉบับที่ 2 มาหลายปีแล้ว) ในเวลานั้นไม่มีอะไรอื่น
  • ส่วนที่สองใน " ซอฟต์แวร์สำหรับการวิเคราะห์ข้อมูล " ของ Chambers ซึ่งเป็นรุ่นล่าสุดและให้ความรู้สึกที่ดีกว่า R-centric มาก - และสองบทเกี่ยวกับการขยาย R ทั้ง C และ C ++ ได้รับการกล่าวถึง นอกจากนี้จอห์นยังทำลายฉันในสิ่งที่ฉันทำด้วยการสรุปข้อมูลเพื่อให้คนเดียวคุ้มค่ากับค่าเข้าชม

ที่กล่าวว่าจอห์นชื่นชอบRcpp (และมีส่วนร่วม) มากขึ้นในขณะที่เขาพบว่าการจับคู่ระหว่างวัตถุ R และวัตถุ C ++ (ผ่านRcpp ) เป็นธรรมชาติมาก - และ ReferenceClasses ช่วยได้ที่นั่น

แก้ไข 2: ด้วยคำถามที่ถูกอ้างถึงของ Hadley ฉันขอให้คุณพิจารณา C ++ เป็นอย่างยิ่ง มีเรื่องไร้สาระสำเร็จรูปมากคือคุณจะต้องทำอย่างไรกับ C --- มากน่าเบื่อและหลีกเลี่ยงมาก มีลักษณะที่เป็นบทความ Rcpp-แนะนำ อีกตัวอย่างง่ายๆคือโพสต์บล็อกนี้ที่ฉันแสดงให้เห็นว่าแทนที่จะกังวลเกี่ยวกับความแตกต่าง 10% (ในตัวอย่างหนึ่งของ Radford Neal) เราจะได้รับC ++ เพิ่มขึ้นแปดสิบเท่า (ซึ่งเป็นตัวอย่างที่สร้างขึ้น)

แก้ไข 3:มีความซับซ้อนที่คุณอาจพบข้อผิดพลาด C ++ นั่นคือต้องใส่อย่างอ่อนโยนและยากที่จะบ่น แต่ในการใช้ Rcppแทนการขยายคุณแทบจะไม่จำเป็นต้องใช้เลย และในขณะที่ค่าใช้จ่ายนี้ไม่สามารถปฏิเสธได้ แต่ก็ถูกบดบังด้วยประโยชน์ของรหัสที่ง่ายกว่า, สำเร็จรูปน้อยกว่า, ไม่มีการป้องกัน / ยกเลิกการป้องกัน, ไม่มีการจัดการหน่วยความจำ ฯลฯ ดั๊กเบตส์เมื่อวานนี้ระบุว่าเขาพบว่า C ++ และ Rcpp นั้นเหมือนกับการเขียน R กว่าการเขียน C ++ YMMV และทั้งหมดนั้น


ฉันคาดหวังว่าฉันจะได้รับคำตอบ "use Rcpp";) มันจะมีประโยชน์มากถ้าคุณสามารถระบุข้อเสียของการใช้ C ++ แทน C สิ่งสำคัญอย่างหนึ่งดูเหมือนว่า C ++ จะซับซ้อนกว่าที่ C - ทำ ทำให้ใช้งานยากขึ้น? (หรือในทางปฏิบัติคุณสามารถเขียนโค้ด C ++ ที่คล้ายกับ C มากได้หรือไม่) ฉันขอขอบคุณข้อมูลอ้างอิงเพิ่มเติมที่มุ่งเป้าไปที่ผู้ใช้ใหม่ที่ไม่คุ้นเคยกับ C api ที่มีอยู่
hadley

2
ดูแก้ไข 3และใช่คุณสามารถ เมเยอร์สเรียก C ++ ว่าเป็นภาษา 'กระบวนทัศน์ทั้งสี่' และคุณไม่จำเป็นต้องใช้ทั้งสี่ การใช้มันเป็น 'แค่ C ที่ดีกว่า' และใช้ Rcpp เป็นกาวกับ R นั้นดีอย่างสมบูรณ์แบบ ไม่มีใครบังคับสไตล์คุณ - นี่ไม่ใช่ Java ;-)
Dirk Eddelbuettel

@ เดิร์ก: ขอบคุณสำหรับรายละเอียด มันทำให้เกิดคำถามในสำนักงานของเรามาก่อนเนื่องจาก C มักใช้ที่นี่แทน C ++ การใช้ C มากกว่า C ++ จะเป็นประโยชน์เมื่อใดหรือคุณเพียงแค่พูดว่า "never C, always C ++"?
Joris Meys

Hadley: เจ๋ง เราสนใจในความคิดเห็นของคุณเป็นอย่างมาก โปรดเข้าร่วม rcpp-devel และอย่าอดกลั้น เรารู้ว่าเราเป็นเอกสารสั้น ๆ - แต่ดวงตาที่สดใหม่สามารถช่วยได้มาก
Dirk Eddelbuettel

6
@hadley นั่นหมายความว่าเราสามารถคาดหวังการปรับปรุงความเร็วในggplot?
aL3xa

56

ฮัดลีย์

คุณสามารถเขียนโค้ด C ++ ที่คล้ายกับรหัส C ได้อย่างแน่นอน

ฉันเข้าใจว่าคุณพูดอะไรเกี่ยวกับ C ++ ที่ซับซ้อนกว่า C นี่คือถ้าคุณต้องการเชี่ยวชาญทุกอย่างไม่ว่าจะเป็นออบเจ็กต์เทมเพลต STL การเขียนโปรแกรมเมตาเทมเพลต ฯลฯ ... คนส่วนใหญ่ไม่ต้องการสิ่งเหล่านี้และสามารถพึ่งพาผู้อื่นได้ ไปเลย การใช้งาน Rcpp นั้นซับซ้อนมาก แต่เพียงเพราะคุณไม่รู้ว่าตู้เย็นของคุณทำงานอย่างไรไม่ได้หมายความว่าคุณจะเปิดประตูและหยิบนมสดไม่ได้ ...

จากการมีส่วนร่วมมากมายของคุณต่อ R สิ่งที่ทำให้ฉันประทับใจคือคุณพบว่า R ค่อนข้างน่าเบื่อ (การจัดการข้อมูลกราฟิกการปรับแต่งสตริง ฯลฯ ... ) เตรียมพร้อมสำหรับเซอร์ไพรส์อื่น ๆ อีกมากมายด้วย C API ภายในของ R ซึ่งเป็นเรื่องที่น่าเบื่อมาก

ในบางครั้งฉันอ่านคู่มือ 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 กล่าวว่ามีตัวอย่างอื่น ๆ ในหลาย ๆ เรื่องที่สะเปะสะปะ เรามักจะชี้ให้ผู้คนไปที่การทดสอบหน่วยของเราเนื่องจากแต่ละคนทดสอบส่วนที่เฉพาะเจาะจงของโค้ดและค่อนข้างอธิบายตนเองได้

ฉันเห็นได้ชัดว่ามีความลำเอียงที่นี่ แต่ฉันอยากจะแนะนำให้ทำความคุ้นเคยกับ Rcpp แทนการเรียนรู้ C API ของ R จากนั้นมาที่รายชื่ออีเมลหากมีบางอย่างไม่ชัดเจนหรือดูเหมือนจะไม่สามารถทำได้กับ Rcpp

อย่างไรก็ตามสิ้นสุดการเสนอขาย

ฉันเดาว่าทั้งหมดขึ้นอยู่กับประเภทของรหัสที่คุณต้องการเขียนในที่สุด

Romain


2
"Rcpp ได้รับการออกแบบมาเพื่อทำให้ส่วนที่น่าเบื่อเหล่านี้ของ API หายไป" = สิ่งที่ฉันกำลังมองหา ขอบคุณ! สิ่งที่จะมีประโยชน์จริงๆคือ v. Brief C ++ primer สำหรับคนที่คุ้นเคยกับ C และต้องการใช้ Rcpp
hadley

ดีตัวอย่างสั้น ๆ ของ Rcpp ทำให้ฉันขายได้ ฉันสมมติว่าจัดสรรXXและ UNPROTECT (1) ได้รับการจัดการเหมือนกับวิธีที่ตัวชี้อัจฉริยะจัดการทรัพยากร เช่น RAII มีการลงโทษประสิทธิภาพที่โดดเด่นโดยใช้ Rcpp กับ vanilla C api หรือไม่?
jbremnant

เรากล่าวถึงสิ่งนั้นในบทนำ Rcpp ด้วยตัวอย่างการเปรียบเทียบ (ซึ่งอยู่ในแหล่งที่มา / แพ็คเกจที่ติดตั้งด้วย) ในระยะสั้นไม่มีโทษเลย
Dirk Eddelbuettel

29

@hadley: น่าเสียดายที่ฉันไม่มีแหล่งข้อมูลเฉพาะที่จะช่วยคุณในการเริ่มต้นใช้งาน C ++ ฉันหยิบมาจากหนังสือของ Scott Meyers (C ++ ที่มีประสิทธิภาพ, C ++ ที่มีประสิทธิภาพมากขึ้น ฯลฯ ... ) แต่สิ่งเหล่านี้ไม่ใช่สิ่งที่เรียกได้ว่าเป็นบทนำ

เราแทบจะใช้อินเทอร์เฟซ. Call เพื่อเรียกรหัส C ++ กฎนั้นง่ายพอ:

  • ฟังก์ชัน C ++ ต้องส่งคืนวัตถุ R วัตถุ R ทั้งหมดคือ SEXP
  • ฟังก์ชัน C ++ ใช้วัตถุระหว่าง 0 ถึง 65 R เป็นอินพุต (SEXP อีกครั้ง)
  • มันจะต้อง (ไม่ได้จริงๆ แต่เราสามารถบันทึกนี้ได้ในภายหลัง) ได้รับการประกาศกับ C เชื่อมโยงทั้งกับ"C" externหรือRcppExportนามแฝงที่กำหนด Rcpp

ดังนั้นฟังก์ชัน. Call จึงถูกประกาศเช่นนี้ในไฟล์ส่วนหัวบางไฟล์:

#include <Rcpp.h>

RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;

และดำเนินการเช่นนี้ในไฟล์. cpp:

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

ไม่มีอะไรอีกมากที่ต้องรู้เกี่ยวกับ R API ที่จะใช้ Rcpp

คนส่วนใหญ่ต้องการจัดการกับเวกเตอร์ตัวเลขใน 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 ดังนั้นตัวอย่างเช่นการรวมค่าของเวกเตอร์ตัวเลขจะเป็นดังนี้:

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-intrduction การใช้งานพื้นฐานอาศัยฟังก์ชัน R API R_PreserveObjectและR_ReleaseObject

มีโทษด้านประสิทธิภาพเนื่องจากการห่อหุ้ม C ++ เราพยายามรักษาสิ่งนี้ให้น้อยที่สุดด้วยการใส่อินไลน์ ฯลฯ ... บทลงโทษมีเพียงเล็กน้อยและเมื่อคุณคำนึงถึงผลประโยชน์ในแง่ของเวลาที่ใช้ในการเขียนและบำรุงรักษาโค้ดก็ไม่เกี่ยวข้อง

การเรียกใช้ฟังก์ชัน R จากคลาส Rcpp ฟังก์ชันจะช้ากว่าการเรียกใช้ eval ด้วย C api โดยตรง นี่เป็นเพราะเราใช้ความระมัดระวังและรวมการเรียกฟังก์ชันไว้ในบล็อก tryCatch เพื่อให้เราจับข้อผิดพลาด R และเลื่อนระดับเป็นข้อยกเว้น C ++ เพื่อให้สามารถจัดการได้โดยใช้การลอง / จับมาตรฐานใน C ++

คนส่วนใหญ่ต้องการใช้เวกเตอร์ (โดยเฉพาะ NumericVector) และโทษก็น้อยมากสำหรับคลาสนี้ ไดเร็กทอรี example / ConvolveBenchmarks มีฟังก์ชัน Convolve ที่มีชื่อเสียงหลายรูปแบบจาก R-exts และบทความสั้นมีผลการเปรียบเทียบ ปรากฎว่า Rcpp ทำให้เร็วกว่ารหัสเปรียบเทียบที่ใช้ R API

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.