Bắt đầu ngayBắt đầu miễn phí

Đừng thay đổi kích thước của vector Rcpp

Các lớp vector của Rcpp được thiết kế như những lớp bao rất mỏng quanh các vector của R. Điều này kéo theo hệ quả về hiệu năng: việc tăng hoặc giảm kích thước của chúng đồng nghĩa với việc tạo một vector mới có kích thước đúng và sao chép dữ liệu liên quan. Đây là thao tác rất chậm, nên cần tránh.

Nếu có thể, bạn nên cấu trúc mã để trước tiên tính kích thước cuối cùng của vector, rồi cấp phát nó với đúng kích thước đó.

Hãy xem một ví dụ chọn các giá trị dương từ một vector, tương đương với x[x > 0] trong R. Vì bạn không biết trước sẽ có bao nhiêu số dương, rất dễ bị cám dỗ bắt đầu với một vector độ dài 0 và nối thêm một giá trị mỗi khi tìm thấy. Ở đây, push_back() là hàm dùng để nối thêm một giá trị.

NumericVector bad_select_positive_values_cpp(NumericVector x) {
  NumericVector positive_x(0);
  for(int i = 0; i < x.size(); i++) {
    if(x[i] > 0) {
      positive_x.push_back(x[i]);
    }
  }
  return positive_x;
}

Đáng tiếc, hàm này sẽ chậm vì nó phải liên tục tạo vector mới và sao chép dữ liệu. Xem bạn có thể làm tốt hơn không!

Bài tập này là một phần của khóa học

Tối ưu hóa mã R với Rcpp

Xem khóa học

Hướng dẫn bài tập

  • Hoàn thiện định nghĩa một hàm hiệu quả hơn, good_select_positive_values_cpp() để chọn các số dương.
    • Ở vòng lặp for đầu tiên, nếu phần tử thứ i của x lớn hơn 0, cộng thêm một vào n_positive_elements.
    • Sau vòng lặp đó, cấp phát một vector số, positive_x, có kích thước n_positive_elements.
    • Ở vòng lặp for thứ hai, tiếp tục kiểm tra xem phần tử thứ i của x có lớn hơn 0 không.
    • Khi điều kiện đúng, gán phần tử thứ j của positive_x bằng phần tử thứ i của x, rồi cộng thêm một vào j.
  • bad_select_positive_values_cpp() đã có sẵn trong không gian làm việc của bạn để so sánh. Xem đầu ra của console để thấy sự khác biệt thời gian chạy khi benchmark.

Bài tập tương tác thực hành trực tiếp

Hãy thử làm bài tập này bằng cách hoàn thành đoạn mã mẫu này.

#include 
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector good_select_positive_values_cpp(NumericVector x) {
  int n_elements = x.size();
  int n_positive_elements = 0;
  
  // Calculate the size of the output
  for(int i = 0; i < n_elements; i++) {
    // If the ith element of x is positive
    if(___) {
      // Add 1 to n_positive_elements
      ___;
    }
  }
  
  // Allocate a vector of size n_positive_elements
  ___;
  
  // Fill the vector
  int j = 0;
  for(int i = 0; i < n_elements; i++) {
    // If the ith element of x is positive
    if(___) {
      // Set the jth element of positive_x to the ith element of x
      ___;
      // Add 1 to j
      ___;
    }
  }
  return positive_x;
}

/*** R
set.seed(42)
x <- rnorm(1e4)
# Does it give the same answer as R?
all.equal(good_select_positive_values_cpp(x), x[x > 0])
# Which is faster?
microbenchmark(
  bad_cpp = bad_select_positive_values_cpp(x),
  good_cpp = good_select_positive_values_cpp(x)
)
*/
Chỉnh sửa và Chạy Mã