Đừ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
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ủaxlớn hơn 0, cộng thêm một vàon_positive_elements. - Sau vòng lặp đó, cấp phát một vector số,
positive_x, có kích thướcn_positive_elements. - Ở vòng lặp
forthứ hai, tiếp tục kiểm tra xem phần tử thứ i củaxcó lớn hơn 0 không. - Khi điều kiện đúng, gán phần tử thứ j của
positive_xbằng phần tử thứ i củax, rồi cộng thêm một vàoj.
- Ở vòng lặp
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)
)
*/