【发布时间】:2013-01-31 03:43:31
【问题描述】:
我的程序计算了风险价值指标的蒙特卡罗模拟。为了尽可能简化,我有:
1/ simulated daily cashflows
2/ to get a sample of a possible 1-year cashflow,
I need to draw 365 random daily cashflows and sum them
因此,每日现金流量是根据经验给出的分布函数,需要进行 365 次抽样。为此,我
1/ sort the daily cashflows into an array called *this->distro*
2/ calculate 365 percentiles corresponding to random probabilities
我需要对年度现金流进行此模拟,例如 10K 次,以获取一组模拟的年度现金流。准备好每日现金流的分布函数后,我会像...
for ( unsigned int idxSim = 0; idxSim < _g.xSimulationCount; idxSim++ )
{
generatedVal = 0.0;
for ( register unsigned int idxDay = 0; idxDay < 365; idxDay ++ )
{
prob = (FLT_TYPE)fastrand(); // prob [0,1]
dIdx = prob * dMaxDistroIndex; // scale prob to distro function size
// to get an index into distro array
_floor = ((FLT_TYPE)(long)dIdx); // fast version of floor
_ceil = _floor + 1.0f; // 'fast' ceil:)
iIdx1 = (unsigned int)( _floor );
iIdx2 = iIdx1 + 1;
// interpolation per se
generatedVal += this->distro[iIdx1]*(_ceil - dIdx );
generatedVal += this->distro[iIdx2]*(dIdx - _floor);
}
this->yearlyCashflows[idxSim] = generatedVal ;
}
for 循环中的代码执行线性插值。如果说 1000 美元对应概率 = 0.01,10000 美元对应概率 = 0.1,那么如果我没有 p = 0.05 的经验数,我想通过插值获得 5000 美元。
问题:这段代码运行正确,尽管分析器说程序在插值本身上花费了大约 60% 的运行时间。所以我的问题是,我怎样才能使这项任务更快? VTune 报告的特定行的示例运行时如下:
prob = (FLT_TYPE)fastrand(); // 0.727s
dIdx = prob * dMaxDistroIndex; // 1.435s
_floor = ((FLT_TYPE)(long)dIdx); // 0.718s
_ceil = _floor + 1.0f; // -
iIdx1 = (unsigned int)( _floor ); // 4.949s
iIdx2 = iIdx1 + 1; // -
// interpolation per se
generatedVal += this->distro[iIdx1]*(_ceil - dIdx ); // -
generatedVal += this->distro[iIdx2]*(dIdx - _floor); // 12.704s
破折号表示分析器没有报告这些行的运行时间。
任何提示将不胜感激。 丹尼尔
编辑: c.fogelklou 和 MSalters 都指出了很大的改进。符合 c.fogelklou 所说的最好的代码是
converter = distroDimension / (FLT_TYPE)(RAND_MAX + 1)
for ( unsigned int idxSim = 0; idxSim < _g.xSimulationCount; idxSim++ )
{
generatedVal = 0.0;
for ( register unsigned int idxDay = 0; idxDay < 365; idxDay ++ )
{
dIdx = (FLT_TYPE)fastrand() * converter;
iIdx1 = (unsigned long)dIdx);
_floor = (FLT_TYPE)iIdx1;
generatedVal += this->distro[iIdx1] + this->diffs[iIdx1] *(dIdx - _floor);
}
}
按照 MSalter 的说法,我拥有的最好的是
normalizer = 1.0/(FLT_TYPE)(RAND_MAX + 1);
for ( unsigned int idxSim = 0; idxSim < _g.xSimulationCount; idxSim++ )
{
generatedVal = 0.0;
for ( register unsigned int idxDay = 0; idxDay < 365; idxDay ++ )
{
dIdx = (FLT_TYPE)fastrand()* normalizer ;
iIdx1 = fastrand() % _g.xDayCount;
generatedVal += this->distro[iIdx1];
generatedVal += this->diffs[iIdx1]*dIdx;
}
}
第二个代码大约是。快 30%。现在,在总运行时间的 95 秒中,最后一行消耗了 68 秒。最后一行只消耗 3.2 秒,因此双 * 双乘法一定是魔鬼。我想到了 SSE - 将最后三个操作数保存到一个数组中,然后执行 this->diffs[i]*dIdx[i] 的向量乘法并将其添加到 this->distro[i] 但这段代码运行了 50%慢点。因此,我想我碰壁了。
非常感谢大家。 D.
【问题讨论】:
-
你有没有解释为什么你的最后一行需要这么多时间,而前一行没有报告时间?除了编译器将它们混合成一个或类似的东西之外,它们应该花费完全相同的时间。
-
Philipp:我只是在转发 VTune 告诉我的内容。我敢打赌,真正的效果是每行分别为 6.3 秒。
-
对于您的标题问题(!= 您的 fastrand 问题),请参阅 Numerical Recipes 第 435-438 页中的“单次分位数估计”,带有绘图和 c++ 代码。
标签: c++ performance percentile