--- title: "std::vector and Rcpp Vectors" author: "Colin Rundel" date: "2019-02-28" output: xaringan::moon_reader: css: "slides.css" lib_dir: libs nature: highlightStyle: github countIncrementalSlides: false --- exclude: true ```{r setup, message=FALSE, warning=FALSE, include=FALSE} library(tidyverse) library(Rcpp) options(tibble.width = 110, width = 110) ``` ```{r global_options} knitr::opts_chunk$set(cache=TRUE) ``` --- ## C Vectors ```{Rcpp} #include // [[Rcpp::export]] void c_vec() { int v[8]; for(int i=0; i!=8; i++) { Rcpp::Rcout << "v[" << i << "] = " << v[i] << "\n"; } } ``` ```{r} c_vec() ``` --- ```{Rcpp} #include // [[Rcpp::export]] void c_vec_bad() { int v[8]; for(int i=0; i!=12; i++) { Rcpp::Rcout << "v[" << i << "] = " << v[i] << "\n"; } } ``` ```{r} c_vec_bad() ``` --- ## C dynamic vectors ```{Rcpp} #include // [[Rcpp::export]] void c_vec_dyn(int n) { int *v = (int *)calloc(n, sizeof(int)); for(int i=0; i!=n; i++) { Rcpp::Rcout << "v[" << i << "] = " << v[i] << "\n"; } Rcpp::Rcout << "\n"; free(v); } ``` ```{r} c_vec_dyn(2) c_vec_dyn(3) ``` --- ## C++ dynamic vectors ```{Rcpp} #include // [[Rcpp::export]] void cpp_vec_dyn(int n) { int *v = new int[n](); for(int i=0; i!=n; i++) { Rcpp::Rcout << "v[" << i << "] = " << v[i] << "\n"; } Rcpp::Rcout << "\n"; delete[] v; } ``` ```{r} cpp_vec_dyn(2) cpp_vec_dyn(3) ``` --- ## C++ stl vectors ```{Rcpp} #include // [[Rcpp::export]] void cpp_stl_vec(int n) { std::vector v(n); for(int i=0; i!=n; i++) { Rcpp::Rcout << "v[" << i << "] = " << v[i] << "\n"; } Rcpp::Rcout << "\n"; } ``` ```{r} cpp_stl_vec(2) cpp_stl_vec(3) ``` --- ## C++ Templates (very briefly) ```{Rcpp} #include #include template std::string what_am_i() { T obj; return typeid(obj).name(); } // [[Rcpp::export]] void template_ex() { Rcpp::Rcout << "bool : " << what_am_i() << "\n"; Rcpp::Rcout << "int : " << what_am_i() << "\n"; Rcpp::Rcout << "long int : " << what_am_i() << "\n"; Rcpp::Rcout << "char : " << what_am_i() << "\n"; Rcpp::Rcout << "float : " << what_am_i() << "\n"; Rcpp::Rcout << "double : " << what_am_i() << "\n"; Rcpp::Rcout << "std::string: " << what_am_i() << "\n"; } ``` ```{r} template_ex() ``` --- ## Initializing std::vector ```{Rcpp} #include // [[Rcpp::plugins(cpp14)]] // [[Rcpp::export]] Rcpp::List init_int_vec() { std::vector v1 = {1, 2, 3}; // Requires cpp14 std::vector v2(2); std::vector v3(2, 1); std::vector v4(v1); std::vector v5(v1.begin(), v1.end()); std::vector v6(v1.begin(), v1.end()-1); std::vector v7; v7.push_back(10); v7.push_back(9); v7.push_back(8); return Rcpp::List::create(v1,v2,v3,v4,v5,v6,v7); } ``` ```{r} str(init_int_vec()) ``` --- ## Initializing std::vector ```{Rcpp} #include // [[Rcpp::plugins(cpp14)]] // [[Rcpp::export]] Rcpp::List init_dbl_vec() { std::vector v1 = {1, 2, 3}; // Requires cpp14 std::vector v2(2); std::vector v3(2, 1); std::vector v4(v1); std::vector v5(v1.begin(), v1.end()); std::vector v6(v1.begin(), v1.end()-1); std::vector v7; v7.push_back(10); v7.push_back(9); v7.push_back(8); return Rcpp::List::create(v1,v2,v3,v4,v5,v6,v7); } ``` ```{r} str(init_dbl_vec()) ``` --- ## Translating std::vector ```{Rcpp} #include // [[Rcpp::export]] Rcpp::IntegerVector from_std_vec(std::vector v) { return Rcpp::IntegerVector(v.begin(), v.end()); } // [[Rcpp::export]] std::vector to_std_vec(Rcpp::IntegerVector rv) { return std::vector(rv.begin(), rv.end()); } ``` ```{r} from_std_vec(1:10) to_std_vec(1:10) ``` --- ## Translating std::vectors (SEXPs) ```{Rcpp} #include // [[Rcpp::export]] SEXP sexp_std_vec(SEXP rv) { std::vector v = Rcpp::as< std::vector >(rv); return Rcpp::wrap(v); } // [[Rcpp::export]] SEXP sexp_rcpp_vec(SEXP rv) { Rcpp::IntegerVector v = Rcpp::as< Rcpp::IntegerVector >(rv); return Rcpp::wrap(v); } ``` ```{r} sexp_std_vec(1:10) sexp_rcpp_vec(1:10) ``` --- ## std::vector methods - access .small[ ```{Rcpp} #include // [[Rcpp::export]] void std_vec_access(std::vector v) { Rcpp::Rcout << "v.front() = " << v.front() << "\n"; Rcpp::Rcout << "v[0] = " << v[0] << "\n"; Rcpp::Rcout << "v.at(0) = " << v.at(0) << "\n"; Rcpp::Rcout << "v.back() = " << v.back() << "\n"; Rcpp::Rcout << "v[5] = " << v[5] << "\n"; Rcpp::Rcout << "v.at(5) = " << v.at(5) << "\n"; Rcpp::Rcout << "\n"; } ``` ```{r error=TRUE} std_vec_access(1:6) std_vec_access(1:5) ``` ] --- ## Rcpp vector methods - access .small[ ```{Rcpp} #include // [[Rcpp::export]] void rcpp_vec_access(Rcpp::IntegerVector v) { Rcpp::Rcout << "v[0] = " << v[0] << "\n"; Rcpp::Rcout << "v(0) = " << v(0) << "\n"; Rcpp::Rcout << "v.at(0) = " << v.at(0) << "\n"; Rcpp::Rcout << "v[5] = " << v[5] << "\n"; Rcpp::Rcout << "v(5) = " << v(5) << "\n"; Rcpp::Rcout << "v.at(5) = " << v.at(5) << "\n"; Rcpp::Rcout << "\n"; } ``` ```{r error=TRUE} rcpp_vec_access(1:6) rcpp_vec_access(1:5) ``` ] --- ## Iterators .tiny[ ```{Rcpp} #include // [[Rcpp::export]] void iterators(int n) { std::vector sv(n); Rcpp::IntegerVector rv(n); std::iota(sv.begin(), sv.end(), 1); std::iota(rv.begin(), rv.end(), 1); Rcpp::Rcout << " sv.begin() : " << &*sv.begin() << "\n"; Rcpp::Rcout << " sv.end() : " << &*sv.end() << "\n"; Rcpp::Rcout << " sv.end() - v.begin() : " << sv.end() - sv.begin() << "\n"; Rcpp::Rcout << "*sv.begin() : " << *sv.begin() << "\n"; Rcpp::Rcout << "*sv.end() : " << *sv.end() << "\n"; Rcpp::Rcout << "*(sv.end()-1) : " << *(sv.end()-1) << "\n"; Rcpp::Rcout << "\n"; Rcpp::Rcout << " rv.begin() : " << &*rv.begin() << "\n"; Rcpp::Rcout << " rv.end() : " << &*rv.end() << "\n"; Rcpp::Rcout << " rv.end() - v.begin() : " << rv.end() - rv.begin() << "\n"; Rcpp::Rcout << "*rv.begin() : " << *rv.begin() << "\n"; Rcpp::Rcout << "*rv.end() : " << *rv.end() << "\n"; Rcpp::Rcout << "*(rv.end()-1) : " << *(rv.end()-1) << "\n"; Rcpp::Rcout << "\n"; } ``` ] .smaller[ ```{r} iterators(100) ``` ] --- ## (More) Iterators .smaller[ ```{Rcpp} #include // [[Rcpp::plugins(cpp11)]] // [[Rcpp::export]] void iterators2(int n) { std::vector sv(n); std::iota(sv.begin(), sv.end(), 1); auto it = sv.begin(); Rcpp::Rcout << " *(sv.begin()) : " << *(sv.begin()) << "\n"; Rcpp::Rcout << " *(sv.begin()++) : " << *(sv.begin()++) << "\n"; Rcpp::Rcout << " *(++sv.begin()) : " << *(++sv.begin()) << "\n"; Rcpp::Rcout << " *(sv.begin()) : " << *(sv.begin()) << "\n"; Rcpp::Rcout << " *(it) : " << *(it) << "\n"; Rcpp::Rcout << " *(it++) : " << *(it++) << "\n"; Rcpp::Rcout << " *(++it) : " << *(++it) << "\n"; Rcpp::Rcout << " *(++it) : " << *(++it) << "\n"; Rcpp::Rcout << " *(it++) : " << *(it++) << "\n"; Rcpp::Rcout << "\n"; } ``` ```{r} iterators2(10) ``` ] --- ## std::vector methods - growing .small[ ```{Rcpp} #include // [[Rcpp::export]] void grow(int n, int m) { std::vector v; for(int i = 0, k = 0; i < n; ++i) { for(int j = 0; j < m; ++j, ++k) { v.push_back(k); } Rcpp::Rcout << "iter: " << k << " size: " << v.size() << " capacity: " << v.capacity() << "\n"; } Rcpp::Rcout << "\n"; } ``` ```{r} grow(10, 2) ``` ] --- ## Growth performance .smaller[ .columns[ .col[ ```{Rcpp} #include // [[Rcpp::export]] int std_vec_grow(int n) { std::vector v; for(int i = 0; i < n; ++i) { v.push_back(i); } return v.size(); } ``` ] .col[ ```{Rcpp} #include // [[Rcpp::export]] int rcpp_grow_back(int n) { Rcpp::IntegerVector v; for(int i = 0; i < n; ++i) { v.push_back(i); } return v.size(); } // [[Rcpp::export]] int rcpp_grow_front(int n) { Rcpp::IntegerVector v; for(int i = 0; i < n; ++i) { v.push_front(i); } return v.size(); } ``` ] ] ] --- .small[ ```{r} bench::mark( std_vec_grow(1e3), rcpp_grow_back(1e3), rcpp_grow_front(1e3) ) ``` ] --- ## Allocation performance .smaller[ .columns[ .col[ ```{Rcpp} #include // [[Rcpp::export]] int std_vec_alloc(int n) { std::vector v(n); for(int i = 0; i < n; ++i) { v[i] = i; } return v.size(); } // [[Rcpp::export]] int rcpp_alloc(int n) { Rcpp::IntegerVector v(n); for(int i = 0; i < n; ++i) { v[i] = i; } return v.size(); } ``` ] .col[ ```{Rcpp} #include // [[Rcpp::export]] int std_vec_alloc_at(int n) { std::vector v(n); for(int i = 0; i < n; ++i) { v.at(i) = i; } return v.size(); } // [[Rcpp::export]] int rcpp_alloc_at(int n) { Rcpp::IntegerVector v(n); for(int i = 0; i < n; ++i) { v.at(i) = i; } return v.size(); } ``` ] ] ] .smaller[ ```{r} bench::mark( std_vec_alloc(1e6), rcpp_alloc(1e6), std_vec_alloc_at(1e6), rcpp_alloc_at(1e6) ) ``` ] --- ## Translation performance .smaller[ ```{Rcpp} #include // [[Rcpp::export]] std::vector std_vec_trans(std::vector v) { return v; } // [[Rcpp::export]] Rcpp::IntegerVector rcpp_vec_trans(Rcpp::IntegerVector v) { return v; } ``` ```{Rcpp} #include // [[Rcpp::export]] std::vector std_vec_trans_ref(std::vector &v) { return v; } // [[Rcpp::export]] Rcpp::IntegerVector rcpp_vec_trans_ref(Rcpp::IntegerVector &v) { return v; } ``` ] --- .small[ ```{r} x = rpois(1e6, 1) bench::mark( std_vec_trans(x), rcpp_vec_trans(x), std_vec_trans_ref(x), rcpp_vec_trans_ref(x) ) ``` ] --- ## Sorting performance .smaller[ ```{Rcpp} #include // [[Rcpp::export]] std::vector std_vec_sort(std::vector v) { std::sort(v.begin(), v.end()); return v; } // [[Rcpp::export]] Rcpp::NumericVector rcpp_vec_sort(Rcpp::NumericVector v) { v.sort(); return v; } ``` ```{r} x = rnorm(1e6) bench::mark( std_vec_sort(x), rcpp_vec_sort(x) ) ``` ] --- ## Insert performance .smaller[ ```{Rcpp} #include // [[Rcpp::export]] std::vector std_vec_ins(std::vector v, int n, int pos, int value) { for(int i=0; i // [[Rcpp::export]] Rcpp::IntegerVector rcpp_vec_ins_it(Rcpp::IntegerVector v, int n, int pos, int value) { for(int i=0; i // [[Rcpp::export]] std::vector std_vec_erase(std::vector v, int pos, int n) { for(int i=0; i std_vec_erase_range(std::vector v, int pos, int n) { v.erase(v.begin()+pos, v.begin()+pos+n); return v; } ``` ```{Rcpp} #include // [[Rcpp::export]] Rcpp::IntegerVector rcpp_vec_erase(Rcpp::IntegerVector v, int pos, int n) { for(int i=0; i // [[Rcpp::export]] double rcpp_rmse(Rcpp::NumericVector y, Rcpp::NumericVector y_hat) { Rcpp::NumericVector diff = y - y_hat; return sqrt( Rcpp::sum(diff*diff) / y.size() ); } ``` ```{r error=TRUE} rcpp_rmse(1:10, 1:10) rcpp_rmse(1:10, 10:1) rcpp_rmse(1:10, 1:5) rcpp_rmse(1:5, 1:10) ``` ] --- ## RMSE - std::vector .smaller[ ```{Rcpp} #include // [[Rcpp::export]] double std_vec_rmse(std::vector y, std::vector y_hat) { double sum = 0.0; for(int i=0; i // [[Rcpp::export]] Rcpp::NumericVector rcpp_len_issue(Rcpp::NumericVector y, Rcpp::NumericVector y_hat) { return y - y_hat; } ``` ```{r} rcpp_len_issue(1:10, 1:10) rcpp_len_issue(1:10, 10:1) rcpp_len_issue(1, 1:10) rcpp_len_issue(1:5, 1:10) ``` ] --- ## Logical Operators and Predicates .smaller[ ```{Rcpp} #include // [[Rcpp::export]] void rcpp_logical() { Rcpp::IntegerVector x = Rcpp::IntegerVector::create(0, 1, NA_INTEGER, 3); Rcpp::LogicalVector l = x > 5; Rcpp::Rcout << "x : " << x << "\n"; Rcpp::Rcout << "x > 2 : " << Rcpp::LogicalVector(x > 2) << "\n"; Rcpp::Rcout << "is_na(x) : " << Rcpp::LogicalVector(Rcpp::is_na(x)) << "\n"; Rcpp::Rcout << "any(is_na(x)) : " << Rcpp::LogicalVector(Rcpp::any(Rcpp::is_na(x))) << "\n"; Rcpp::Rcout << "all(x < 5) : " << Rcpp::LogicalVector(Rcpp::all(x < 5)) << "\n"; Rcpp::Rcout << "ifelse(is_na(x), -1, x) : " << Rcpp::IntegerVector(Rcpp::ifelse(Rcpp::is_na(x), -1, x)) << "\n"; } ``` ```{r} rcpp_logical() ``` ]