我的程序针对风险值指标计算蒙特卡罗模拟。为了尽可能地简化,我有:
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美元对应于prob = 0.01,则10000美元对应于prob = 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
Dashes表示分析器报告这些行没有运行时。
任何提示都将不胜感激。 丹尼尔
修改 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%。现在,在总运行时间的95s中,最后一行消耗68s。最后一行只消耗3.2s,因此double * double乘法必须是魔鬼。我想到SSE - 将最后三个操作数保存到一个数组中然后执行这个 - > diffs [i] * dIdx [i]的向量乘法并将其添加到this-&gt; distro [i]但是这个代码运行了减慢50%。因此,我认为我碰壁了。
非常感谢所有人。 d。
答案 0 :(得分:4)
这是一个小优化的提议,不再需要ceil,两个演员阵容和一个倍数。如果你在一个定点处理器上运行,这可以解释为什么float和int之间的乘法和转换需要这么长时间。在这种情况下,如果CPU支持,请尝试使用定点优化或打开编译器中的浮点数!
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
iIdx1 = (long)dIdx;
_floor = (FLT_TYPE)iIdx1; // fast version of floor
iIdx2 = iIdx1 + 1;
// interpolation per se
{
const FLT_TYPE diff = this->distro[iIdx2] - this->distro[iIdx1];
const FLT_TYPE interp = this->distro[iIdx1] + diff * (dIdx - _floor);
generatedVal += interp;
}
}
this->yearlyCashflows[idxSim] = generatedVal ;
}
答案 1 :(得分:1)
我建议修复fastrand
。浮点代码不是世界上最快的,但特别慢的是浮点和整数代码之间的切换。由于您需要整数索引,因此请使用整数随机函数。
在循环中预先生成所有365个随机值甚至可能是有利的。由于每个值只需要log2(dMaxDistroIndex)
个随机性位,因此您可以减少RNG呼叫的数量。
随后您将为插值分数选择介于0和1之间的随机数。