2014-05-02 7 views
5

Ho scritto i seguenti codici in R e C++ che eseguono lo stesso algoritmo:Perché il mio codice C++ è molto più lento di R?

a) Per simulare la variabile casuale X 500 volte. (X ha valore 0.9 con prob 0.5 e 1.1 con prob 0.5)

b) Moltiplica questi 500 valori simulati insieme per ottenere un valore. Salvare il valore in un contenitore

c) Ripetere 10000000 volte in modo che il contenitore ha 10000000 valori

R:

ptm <- proc.time() 
steps <- 500 
MCsize <- 10000000 
a <- rbinom(MCsize,steps,0.5) 
b <- rep(500,times=MCsize) - a 
result <- rep(1.1,times=MCsize)^a*rep(0.9,times=MCsize)^b 
proc.time()-ptm 

C++

#include <numeric> 
#include <vector> 
#include <iostream> 
#include <random> 
#include <thread> 
#include <mutex> 
#include <cmath> 
#include <algorithm> 
#include <chrono> 

const size_t MCsize = 10000000; 
std::mutex mutex1; 
std::mutex mutex2; 
unsigned seed_; 
std::vector<double> cache; 

void generatereturns(size_t steps, int RUNS){ 
    mutex2.lock(); 
    // setting seed 
    try{  
     std::mt19937 tmpgenerator(seed_); 
     seed_ = tmpgenerator(); 
     std::cout << "SEED : " << seed_ << std::endl; 
    }catch(int exception){ 
     mutex2.unlock(); 
    } 
    mutex2.unlock(); 

    // Creating generator 
    std::binomial_distribution<int> distribution(steps,0.5); 
    std::mt19937 generator(seed_); 

    for(int i = 0; i!= RUNS; ++i){ 
     double power; 
     double returns; 
     power = distribution(generator); 
     returns = pow(0.9,power) * pow(1.1,(double)steps - power); 
     std::lock_guard<std::mutex> guard(mutex1); 
     cache.push_back(returns); 
    } 
}  


int main(){ 
    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); 
    size_t steps = 500; 
    seed_ = 777;  

    unsigned concurentThreadsSupported = std::max(std::thread::hardware_concurrency(),(unsigned)1); 
    int remainder = MCsize % concurentThreadsSupported; 

    std::vector<std::thread> threads; 
    // starting sub-thread simulations 
    if(concurentThreadsSupported != 1){ 
     for(int i = 0 ; i != concurentThreadsSupported - 1; ++i){ 
      if(remainder != 0){ 
       threads.push_back(std::thread(generatereturns,steps,MCsize/ concurentThreadsSupported + 1)); 
       remainder--; 
      }else{ 
       threads.push_back(std::thread(generatereturns,steps,MCsize/ concurentThreadsSupported)); 
      } 
     } 
    } 

    //starting main thread simulation 
    if(remainder != 0){ 
     generatereturns(steps, MCsize/concurentThreadsSupported + 1); 
     remainder--; 
    }else{ 
     generatereturns(steps, MCsize/concurentThreadsSupported); 
    } 

    for (auto& th : threads) th.join(); 

    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now() ; 
    typedef std::chrono::duration<int,std::milli> millisecs_t ; 
    millisecs_t duration(std::chrono::duration_cast<millisecs_t>(end-start)) ; 
    std::cout << "Time elapsed : " << duration.count() << " milliseconds.\n" ; 

    return 0; 
} 

non riesco a capire perché il mio Il codice R è molto più veloce del mio codice C++ (3.29s vs 12s) anche se ho usato quattro thread nel codice C++? Qualcuno può illuminarmi per favore? Come dovrei migliorare il mio codice C++ per farlo funzionare più velocemente?

MODIFICA:

Grazie per tutti i consigli! Ho riservato capacità per i miei vettori e ridotto la quantità di blocchi nel mio codice. L'aggiornamento cruciale nelle generatereturns() è:

std::vector<double> cache(MCsize); 
std::vector<double>::iterator currit = cache.begin(); 
//..... 

// Creating generator 
std::binomial_distribution<int> distribution(steps,0.5); 
std::mt19937 generator(seed_); 
std::vector<double> tmpvec(RUNS); 
for(int i = 0; i!= RUNS; ++i){ 
    double power; 
    double returns; 
    power = distribution(generator); 
    returns = pow(0.9,power) * pow(1.1,(double)steps - power); 
    tmpvec[i] = returns; 
} 
std::lock_guard<std::mutex> guard(mutex1); 
std::move(tmpvec.begin(),tmpvec.end(),currit); 
currit += RUNS; 

Invece di bloccaggio ogni volta, ho creato un vettore temporaneo e poi utilizzato std :: mossa per spostare gli elementi che tempvec nella cache. Ora il tempo trascorso è ridotto a 1,9 secondi.

+0

compilatore? OS?impostazioni del compilatore? macchina? Siamo spiacenti, non ci sono informazioni per noi! – Klaus

+0

Sembra che si stia facendo un eccessivo blocco nel codice. Hai provato una versione senza thread, prima? In caso contrario, dovresti provare a scrivere l'output su vettori separati <> che non devono essere bloccati, combinando l'output alla fine. Prova anche a riservare capacità per i tuoi vettori. –

+0

Uso Visual Studio Express con Windows 8. Il mio processore è Intel i5 con quadcore. – user22119

risposta

2

Prima di tutto, lo stai eseguendo in modalità di rilascio? Il passaggio da debug a release ha ridotto il tempo di esecuzione da ~ 15 s a ~ 4,5 s sul mio laptop (Windows 7, iM).

Inoltre, riducendo il numero di thread a 2 invece di 4 nel mio caso (ho solo 2 core ma con hyperthreading) ulteriormente ridotto il tempo di esecuzione a ~ 2.4s.

Cambiare la potenza variabile su int (come suggerito anche da jimifiki) ha anche offerto un leggero incremento, riducendo il tempo a ~ 2.3s.

1

Probabilmente non ti aiuta molto, ma inizia usando pow (double, int) quando il tuo esponente è un int.

int power; 
returns = pow(0.9,power) * pow(1.1,(int)steps - power); 

Riesci a vedere qualche miglioramento?

1

Mi è piaciuta molto la tua domanda e ho provato il codice a casa. Ho provato a cambiare il generatore di numeri casuali, la mia implementazione di std :: binomial_distribution richiede in media circa 9.6 chiamate di generator().

So che la domanda è più sul confronto di R con le prestazioni C++, ma dal momento che si chiede "Come dovrei migliorare il mio codice C++ per farlo funzionare più velocemente?" Insisto con l'ottimizzazione della pow. È possibile evitare facilmente metà della chiamata anticipando i passi 0,9^o 1.1^prima del ciclo for. Questo rende il vostro codice di correre un po 'più veloce:

double power1 = pow(0.9,steps); 
double ratio = 1.1/0.9; 
for(int i = 0; i!= RUNS; ++i){ 
    ... 
    returns = myF1 * pow(myF2, (double)power); 

Analogamente si può migliorare il codice R:

... 
ratio <-1.1/0.9 
pow1 = 0.9^steps 
result <- rep(ratio,times=MCsize)^rep(pow1,times=MCsize) 
... 
Problemi correlati