2015-10-15 14 views
5

Come il titolo della domanda dice, mi piacerebbe sapere perché il byte di codice compilato R (usando compiler::cmpfun) è più veloce di codice Rcpp equivalente per la seguente funzione matematica:Perché questo codice Rcpp è più lento del byte R compilato?

func1 <- function(alpha, tau, rho, phi) { 
    abs((alpha + 1)^(tau) * phi - rho * (1- (1 + alpha)^(tau))/(1 - (1 + alpha))) 
} 

Dal momento che si tratta di una semplice operazione numerica , Mi sarei aspettato che Rcpp (funcCpp e funcCpp2) fosse molto più veloce del byte R compilato (func1c e func2c), specialmente dal momento che R avrebbe avuto un sovraccarico maggiore per l'archiviazione di (1+alpha)**tau o avrebbe dovuto ricalcolarlo. In effetti, il calcolo di questo esponente due volte sembra più rapido dell'allocazione di memoria in R(), che sembra particolarmente controintuitivo, dal momento che lo n è di grandi dimensioni. La mia altra ipotesi è che forse lo compiler::cmpfun stia facendo un po 'di magia, ma mi piacerebbe sapere se è davvero così.

Quindi, in realtà, le due cose che mi piacerebbe sapere è:

  1. Perché funcCpp e funcCpp2 più lento di func1c e func2c? (Rcpp più lento delle funzioni R compilate)

  2. Perché funcCpp è più lento di func2? (Codice Rcpp più lento di pura R)

FWIW, ecco la mia C++ e la versione R dati

user% g++ --version 
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 
Apple LLVM version 7.0.0 (clang-700.0.72) 
Target: x86_64-apple-darwin14.3.0 
Thread model: posix 

user% R --version 
R version 3.2.2 (2015-08-14) -- "Fire Safety" 
Copyright (C) 2015 The R Foundation for Statistical Computing 
Platform: x86_64-apple-darwin14.5.0 (64-bit) 

Ed ecco la R e Rcpp codice:

library(Rcpp) 
library(rbenchmark) 

func1 <- function(alpha, tau, rho, phi) { 
    abs((1 + alpha)^(tau) * phi - rho * (1- (1 + alpha)^(tau))/(1 - (1 + alpha))) 
} 

func2 <- function(alpha, tau, rho, phi) { 
    pval <- (alpha + 1)^(tau) 
    abs(pval * phi - rho * (1- pval)/(1 - (1 + alpha))) 
} 

func1c <- compiler::cmpfun(func1) 
func2c <- compiler::cmpfun(func2) 

func3c <- Rcpp::cppFunction(' 
    double funcCpp(double alpha, int tau, double rho, double phi) { 
     double pow_val = std::exp(tau * std::log(alpha + 1.0)); 
     double pAg = rho/alpha; 
     return std::abs(pow_val * (phi - pAg) + pAg); 
    }') 

func4c <- Rcpp::cppFunction(' 
    double funcCpp2(double alpha, int tau, double rho, double phi) { 
     double pow_val = pow(alpha + 1.0, tau) ; 
     double pAg = rho/alpha; 
     return std::abs(pow_val * (phi - pAg) + pAg); 
    }') 

res <- benchmark(
      func1(0.01, 200, 100, 1000000), 
      func1c(0.01, 200, 100, 1000000), 
      func2(0.01, 200, 100, 1000000), 
      func2c(0.01, 200, 100, 1000000), 
      func3c(0.01, 200, 100, 1000000), 
      func4c(0.01, 200, 100, 1000000), 
      funcCpp(0.01, 200, 100, 1000000), 
      funcCpp2(0.01, 200, 100, 1000000), 
      replications = 100000, 
      order='relative', 
      columns=c("test", "replications", "elapsed", "relative")) 

Ed ecco l'output di rbenchmark:

       test replications elapsed relative 
    func1c(0.01, 200, 100, 1e+06)  100000 0.349 1.000 
    func2c(0.01, 200, 100, 1e+06)  100000 0.372 1.066 
funcCpp2(0.01, 200, 100, 1e+06)  100000 0.483 1.384 
    func4c(0.01, 200, 100, 1e+06)  100000 0.509 1.458 
    func2(0.01, 200, 100, 1e+06)  100000 0.510 1.461 
    funcCpp(0.01, 200, 100, 1e+06)  100000 0.524 1.501 
    func3c(0.01, 200, 100, 1e+06)  100000 0.546 1.564 
    func1(0.01, 200, 100, 1e+06)  100000 0.549 1.573K 
+7

Questo 'func1c' è più veloce di' func2c' è probabilmente dovuto al garbage collector o ad altre cause difficili da determinare. Esegui più volte la chiamata "benchmark" e vedrai queste due funzioni cambiare posizione nella classifica. Cercare di misurare qualcosa che richiede solo 1 microsecondo per essere eseguito è molto difficile. –

+2

@bunk "È davvero sorprendente che le funzioni C siano più veloci del C++?" - Sì, assolutamente. Non c'è motivo di aspettarselo. In effetti, il codice C++ può essere spesso reso * più veloce * rispetto al codice C dello stesso livello di astrazione e non dovrebbe mai essere più lento. Ecco ulteriori informazioni: http: //programmers.stackexchange.it/a/29136/2366 –

+0

@bunk Vero, sono dodici contro una dozzina. Non ci dovrebbero essere differenze in alcun modo. La "magia" che Rcpp gira potrebbe ovviamente incorrere in un sovraccarico - tuttavia, anche 'UN'/'PROTECT' viene chiamato qui? La mia conoscenza dell'API di R's C è alquanto imprecisa ma, poiché questi sono tutti argomenti, non dovrebbe esserci alcuna protezione necessaria, dovrebbe esserci? –

risposta

5

Questa è essenzialmente una domanda mal posta. Quando si POSIT

func1 <- function(alpha, tau, rho, phi) { 
    abs((alpha + 1)^(tau) * phi - rho * (1- (1 + alpha)^(tau))/(1 - (1 + alpha))) 
} 

senza nemmeno specificare quali gli argomenti sono (cioè scalare? Vettore? Grande? Piccolo? Overhead di memoria) allora si può nel migliore dei casi solo ottenere un piccolo insieme di (base, efficienti) chiamate di funzione direttamente dall'espressione analizzata.

E da quando abbiamo avuto il compilatore di byte, che è stato migliorato da Luke Tierney nelle versioni R successive, abbiamo saputo che fa espressioni algebriche bene.

codice Ora, compilato C/C++ lo fa bene anche - ma non ci sarà in testa nel chiamare il coed compilato e quello che vedete qui è che per "rtivial abbastanza" problemi, l'overhead non è davvero ottenere ammortizzato.

Quindi si finisce con un pareggio. Non sorpresa per quanto posso dire.

+0

"ma ci sarà un overhead nel chiamare il coed compilato" - Penso che questo sia il punto cruciale del problema. Quante spese generali ci sono? Perché chiamare una funzione R convenzionale ovviamente comporta anche un sovraccarico, e * tutte * le singole operazioni matematiche nella suddetta funzione R incorrono in questo overhead. Quindi dovrebbe essere * molto * più grande in, in somma, del singolo overhead di chiamare la funzione compilata C++ una volta. –

+0

Perché non lo misuri? Inizia con 'Rcpp :: cppFunction ('void empty() {}')' e vai da lì ... –

+0

Non è questo il punto. Se lo dici, ti credo. Mi piacerebbe capire * perché *, e misurare non mi aiuta lì. –

Problemi correlati