C ++中的快速百分位数

时间:2013-02-15 07:33:42

标签: c++ performance percentile

我的程序针对风险值指标计算蒙特卡罗模拟。为了尽可能地简化,我有:

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。

2 个答案:

答案 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之间的随机数。