Новая функция `find_finite` в Armadillo в 3,5 раза медленнее, чем цикл?

новые функции find_finite и find_nonfinite в Armadillo 4.300 — отличное дополнение! В моих тестах с использованием Rcpp они примерно в 2,5 раза медленнее по сравнению со стандартным циклом. Ниже приведен некоторый код для вычисления суммы и среднего значения с удалением по регистру, соответствующим опции R na.rm=TRUE. Тесты производительности из R показывают, что первая версия (sum_arma и mean_arma) примерно в 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

person user2503795    schedule 07.05.2014    source источник


Ответы (1)


Общие функции 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 попытается избежать генерации временных векторов/матриц за счет использования интеллектуальной инфраструктуры отложенных операций (на основе шаблонных выражений), которая ставит в очередь и объединяет несколько операций перед выполнением. их. Это уменьшает генерацию временных элементов.

Реализация отложенных операций довольно сложна, поэтому в основном это делается для наиболее важных арифметических функций. Однако у Armadillo он есть и в нескольких других случаях, например, find(X > 123) не будет генерировать временное для X > 123.

person mtall    schedule 08.05.2014