我在R和C ++中编写了以下代码,它们执行相同的算法:
a)模拟随机变量X 500次。 (X值为0.9,概率为0.5,1.1值为0.5)
b)将这500个模拟值相乘得到一个值。将该值保存在容器中
c)重复10000000次,使容器具有10000000个值
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;
}
我无法理解为什么我的R代码比我的C ++代码(3.29s vs 12s)快得多,即使我在C ++代码中使用了四个线程?请问有人可以开导我吗?我应该如何改进我的C ++代码以使其运行得更快?
编辑:
感谢您的所有建议!我保留了向量的容量,减少了代码中的锁定量。 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;
我不是每次都锁定,而是创建了一个临时向量,然后使用std :: move将tempvec中的元素移动到缓存中。现在经过的时间减少到1.9秒。
答案 0 :(得分:2)
首先,您是否在发布模式下运行它? 从调试到发布的切换将我的笔记本电脑(Windows 7,i5 3210M)上的运行时间从大约15秒缩短到了大约4.5秒。
另外,在我的情况下,将线程数减少到2而不是4(我只有2个内核,但有超线程),进一步将运行时间减少到~2.4s。
将变量幂更改为int(如jimifiki也建议)也提供了轻微的提升,将时间缩短到~2.3秒。
答案 1 :(得分:1)
可能对你没那么帮助,但是 当指数为int时,首先使用pow(double,int)。
int power;
returns = pow(0.9,power) * pow(1.1,(int)steps - power);
你能看到任何进步吗?
答案 2 :(得分:1)
我非常喜欢你的问题,我在家里尝试了这些代码。我试图改变随机数生成器,我的std :: binomial_distribution实现平均需要9.6次调用generator()。
我知道问题更多的是将R与C ++性能进行比较,但是因为你要问&#34;我应该如何改进我的C ++代码以使其运行得更快?&#34;我坚持战俘优化。您可以通过在for循环之前预先计算0.9 ^步或1.1 ^步来轻松避免一半的呼叫。这使您的代码运行得更快:
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);
类似地,您可以改进R代码:
...
ratio <-1.1/0.9
pow1 = 0.9^steps
result <- rep(ratio,times=MCsize)^rep(pow1,times=MCsize)
...