2014-05-07 42 views
2

新的find_finitefind_nonfinite功能在犰狳4.300是很好的補充!在我使用Rcpp的測試中,它們比標準循環慢大約2.5倍。以下是一些代碼,用於計算與R的na.rm=TRUE選項對應的案例刪除的總和和平均值。來自R的性能基準測試表明,第一個版本(sum_armamean_arma)比迴路快大約3.5倍。我做的一切正確嗎?任何方式來提高性能?Armadillo的新find_finite函數比循環慢3.5倍?

C++代碼

#include <numeric> 
#include <RcppArmadillo.h> 
// [[Rcpp::depends(RcppArmadillo)]] 


// [[Rcpp::export]] 
double sum_arma1(arma::mat& X) { 
    double sum = 0; 
    for (int i = 0; i < X.size(); ++i) { 
     if (arma::is_finite(X(i))) 
      sum += X(i); 
    } 
    return sum; 
} 
// [[Rcpp::export]] 
double sum_arma2(arma::mat& X) { 
    return arma::sum(X.elem(arma::find_finite(X))); 
} 

// [[Rcpp::export]] 
double mean_arma1(arma::mat& X) { 
    double sum = 0; 
    int n = 0; 
    for (int i = 0; i < X.size(); ++i) { 
     if (arma::is_finite(X(i))) { 
      sum += X(i); 
      n += 1; 
     } 
    } 
    return sum/n; 
} 
// [[Rcpp::export]] 
double mean_arma2(arma::mat& X) { 
    return arma::mean(X.elem(arma::find_finite(X))); 
} 

來自R基準測試結果

# data 
X = matrix(rnorm(1e6),1000,1000) 
X[sample(1:1000,100),sample(1:1000,100)] = NA 
# equal? 
all.equal(sum(X, na.rm=TRUE),sum_arma1(X)) 
all.equal(sum(X, na.rm=TRUE),sum_arma2(X)) 
all.equal(mean(X, na.rm=TRUE),mean_arma1(X)) 
all.equal(mean(X, na.rm=TRUE),mean_arma2(X)) 

# benchmark 
benchmark(
    sum(X, na.rm=TRUE), 
    sum_arma1(X), 
    sum_arma2(X), 
    replications=100) 

#     test replications elapsed relative user.self sys.self 
# 2   sum_arma1(X)   100 0.259 1.000  0.259 0.001 
# 3   sum_arma2(X)   100 1.035 3.996  0.750 0.293 
# 1 sum(X, na.rm = TRUE)   100 0.491 1.896  0.492 0.003 

benchmark(
    mean(X, na.rm=TRUE), 
    mean_arma1(X), 
    mean_arma2(X), 
    replications=100) 

#     test replications elapsed relative user.self sys.self 
# 2   mean_arma1(X)   100 0.252  1.00  0.253 0.001 
# 3   mean_arma2(X)   100 0.819  3.25  0.620 0.206 
# 1 mean(X, na.rm = TRUE)   100 7.440 29.52  7.120 0.373 

回答

2

的一般功能和find_finite()find_nonfinite()將總是慢於專門求和迴路。 find_finite()不是專門爲求和而設計的,但是對於一般情況,找到有限值的指數。你對這些指標所做的事情取決於你,並且你已經選擇使用它們作爲.elem()函數的輸入。

在代碼arma::sum(X.elem(arma::find_finite(X)))中,函數find_finite()必須經過X,查找有限的值,並將得到的有限值的索引存儲在臨時向量中。 .elem()成員函數然後查看由find_finite()生成的向量,並創建另一個只包含有限值的向量。接着,.elem()生成的向量然後由sum()使用。

C++允許抽象,使您的代碼非常緊湊,但有時您必須爲這種抽象支付費用。一般函數總是比專用循環慢。但是,對於諸如加法,乘法等的算術函數,Armadillo將嘗試通過使用智能延遲操作框架(基於模板表達式)來避免生成臨時向量/矩陣,該框架將隊列化並組合執行它們之前的幾個操作。這減少了臨時的生成。

延遲操作的實現非常複雜,這就是爲什麼它主要爲最重要的算術函數完成的原因。但是,犰狳也有其他一些情況,例如,find(X > 123)將避免產生X > 123的臨時性。