Napisałem następujące kody w R i C++, które wykonują ten sam algorytm:Dlaczego mój kod C++ jest o wiele wolniejszy od R?
a) Aby zasymulować zmienną losową X 500 razy. (X ma wartość 0.9 z prob 0.5 i 1.1 z prob 0.5)
b) Pomnożyć te 500 symulowanych wartości, aby uzyskać wartość. Zapisać tę wartość w pojemniku
c) Powtórz 10000000 czasy takie, że pojemnik ma 10000000 wartości
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;
}
nie mogę zrozumieć, dlaczego mój Kod R jest znacznie szybszy niż mój kod C++ (3.29s vs 12s), mimo że użyłem czterech wątków w kodzie C++? Czy ktoś może mnie oświecić, proszę? Jak mogę poprawić mój kod C++, aby działał szybciej?
EDYTOWANIE:
Dzięki za radę! Zarezerwowałem pojemność dla moich wektorów i zmniejszyłem ilość blokowania w moim kodzie. Kluczowa zmiana w generatereturns() jest:
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;
Zamiast blokowania za każdym razem, stworzyłem tymczasową wektor, a następnie wykorzystywane std :: ruch przesunięcie elementów w tej tempvec do pamięci podręcznej. Czas, który upłynął, został skrócony do 1,9 sekundy.
kompilator? OS?ustawienia kompilatora? maszyna? Przepraszamy, nie ma dla nas informacji! – Klaus
Wygląda na to, że robisz dużo nadmiernego blokowania w swoim kodzie. Czy próbowałeś najpierw wersji bez wątków? W przeciwnym razie powinieneś spróbować zapisać wynik w oddzielnych wektorach <>, które nie muszą być blokowane, łącząc dane wyjściowe na samym końcu. Spróbuj także rezerwować pojemność dla swoich wektorów. –
Używam Visual Studio Express z Windows 8. Mój procesor to intel i5 z quadcore. – user22119