Как уже упоминалось другими, разрешение другим пакетам вызывать ваш код C ++ из C ++ требует использования заголовочных файлов в inst/include/
. Rcpp::interfaces
позволяет автоматизировать создание таких файлов. Однако, как я покажу ниже, создание собственных заголовков может привести к ускорению выполнения. Я полагаю, это потому, что использование Rcpp::interfaces
для создания заголовков может привести к более сложному коду заголовка.
Прежде чем я продолжу и продемонстрирую «более простой» подход, который приводит к более быстрому времени выполнения, я должен отметить, что хотя это работает для меня (и я использовал подход, который я продемонстрирую ниже несколько раз без проблем), тем больше «комплексный» подход, принятый Rcpp::interfaces
, частично используется для сопоставления с утверждениями в Раздел 5.4.3. Руководства по написанию расширений R . (В частности, биты, имеющие отношение к R_GetCCallable
, вы увидите ниже). Итак, улучшите время выполнения с помощью кода, который я предлагаю ниже, на свой страх и риск. 1,2
Простой заголовок для совместного использования кода для col_sums
может выглядеть следующим образом:
#ifndef RCPP_package3
#define RCPP_package3
#include <RcppArmadillo.h>
namespace package3 {
inline arma::vec col_sums(const arma::mat& test){
return arma::sum(test,0).t();
}
}
#endif
Однако заголовок, созданный Rcpp::interfaces
, выглядит следующим образом:
// Generated by using Rcpp::compileAttributes() -> do not edit by hand
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
#ifndef RCPP_package1_RCPPEXPORTS_H_GEN_
#define RCPP_package1_RCPPEXPORTS_H_GEN_
#include <RcppArmadillo.h>
#include <Rcpp.h>
namespace package1 {
using namespace Rcpp;
namespace {
void validateSignature(const char* sig) {
Rcpp::Function require = Rcpp::Environment::base_env()["require"];
require("package1", Rcpp::Named("quietly") = true);
typedef int(*Ptr_validate)(const char*);
static Ptr_validate p_validate = (Ptr_validate)
R_GetCCallable("package1", "_package1_RcppExport_validate");
if (!p_validate(sig)) {
throw Rcpp::function_not_exported(
"C++ function with signature '" + std::string(sig) + "' not found in package1");
}
}
}
inline arma::vec col_sums(const arma::mat& matty) {
typedef SEXP(*Ptr_col_sums)(SEXP);
static Ptr_col_sums p_col_sums = NULL;
if (p_col_sums == NULL) {
validateSignature("arma::vec(*col_sums)(const arma::mat&)");
p_col_sums = (Ptr_col_sums)R_GetCCallable("package1", "_package1_col_sums");
}
RObject rcpp_result_gen;
{
RNGScope RCPP_rngScope_gen;
rcpp_result_gen = p_col_sums(Shield<SEXP>(Rcpp::wrap(matty)));
}
if (rcpp_result_gen.inherits("interrupted-error"))
throw Rcpp::internal::InterruptedException();
if (Rcpp::internal::isLongjumpSentinel(rcpp_result_gen))
throw Rcpp::LongjumpException(rcpp_result_gen);
if (rcpp_result_gen.inherits("try-error"))
throw Rcpp::exception(Rcpp::as<std::string>(rcpp_result_gen).c_str());
return Rcpp::as<arma::vec >(rcpp_result_gen);
}
}
#endif // RCPP_package1_RCPPEXPORTS_H_GEN_
Итак, я создал два дополнительных пакета через
library(RcppArmadillo)
RcppArmadillo.package.skeleton(name = "package3", example_code = FALSE)
RcppArmadillo.package.skeleton(name = "package4", example_code = FALSE)
Затем в package3/inst/include
я добавил package3.h
, содержащий приведенный выше код «простого заголовка» (я также добавил одноразовый cpp-файл «Hello World» в src/
). В package4/src/
я добавил следующее:
#include <package3.h>
// [[Rcpp::export]]
arma::vec col_sums(const arma::mat& test){
return arma::sum(test,0).t();
}
// [[Rcpp::export]]
arma::vec simple_header_import(const arma::mat& test){
return package3::col_sums(test);
}
, а также добавление package3
к LinkingTo
в файле DESCRIPTION
.
Затем, после установки новых пакетов, я сравнил все функции друг с другом:
library(rbenchmark)
set.seed(1)
nr <- 100
p <- 800
testmat <- matrix(rnorm(nr * p), ncol = p)
benchmark(original = package1::col_sums(testmat),
first_copy = package2::col_sums(testmat),
complicated_import = package2::col_sums_imported(testmat),
second_copy = package4::col_sums(testmat),
simple_import = package4::simple_header_import(testmat),
replications = 1e3,
columns = c("test", "relative", "elapsed", "user.self", "sys.self"),
order = "relative")
test relative elapsed user.self sys.self
2 first_copy 1.000 0.174 0.174 0.000
4 second_copy 1.000 0.174 0.173 0.000
5 simple_import 1.000 0.174 0.174 0.000
1 original 1.126 0.196 0.197 0.000
3 complicated_import 6.690 1.164 0.544 0.613
В то время как более «сложная» функция заголовка была в 6 раз медленнее, «более простая» - нет.
1. Тем не менее, автоматический код, сгенерированный Rcpp::interfaces
, на самом деле включает некоторые функции, которые могут оказаться для вас излишними, помимо проблемы R_GetCCallable
, хотя они могут быть желательны и в некоторых других контекстах необходимы.
2. Регистрация функций всегда переносима, и авторы пакетов получают инструкции об этом в руководстве Writing R Extensions, но для внутреннего / организационного / и т. Д. Использование Я считаю, что подход, представленный здесь, должен работать, если все задействованные пакеты собраны из исходного кода. См. этот раздел R пакетов Хэдли Уикхема для некоторого обсуждения, а также раздел руководства по написанию расширений R, связанный выше.