2

私のプログラムは、バリューアットリスクメトリックのモンテカルロシミュレーションを計算します。できるだけ単純化するために、私は次のことを行います。

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

シミュレートされた年間キャッシュフローの母集団を処理するには、この年間キャッシュフローのシミュレーションをたとえば1万回実行する必要があります。毎日のキャッシュフローの分布関数を用意して、次のようなサンプリングを行います...

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両方のサイクル内のコードは線形補間を行います。たとえば、USD1000がprob= 0.01に対応し、USD10000がprob= 0.1に対応する場合、p = 0.05の経験値がない場合は、補間によってUSD5000を取得します。

質問:このコードは正しく実行されますが、プロファイラーによると、プログラムは実行時間の約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;
    }
}

2番目のコードは約です。30パーセント速くなります。現在、合計実行時間の95秒のうち、最後の行は68秒を消費します。最後の1行は3.2秒しか消費しないため、double*double乗算は悪魔である必要があります。私はSSEについて考えました-最後の3つのオペランドを配列に保存してから、this-> diffs [i] * dIdx [i]のベクトル乗算を実行し、これをthis-> distro [i]に追加しますが、このコードは50%実行されましたもっとゆっくり。したがって、私は壁にぶつかったと思います。

みんなに感謝します。D。

4

2 に答える 2

5

これは、小さな最適化の提案であり、天井、2つのキャスト、および1つの乗算の必要性を排除します。固定小数点プロセッサで実行している場合、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 ;
}
于 2013-02-15T07:54:55.123 に答える
2

修正することをお勧めしますfastrand。浮動小数点コードは世界で最速ではありませんが、特に遅いのは浮動小数点コードと整数コードの切り替えです。整数インデックスが必要なため、整数ランダム関数を使用してください。

ループ内の365個のランダムな値すべてを事前に生成することも有利な場合があります。値ごとに必要なlog2(dMaxDistroIndex)ランダム性はわずかであるため、RNG呼び出しの数を減らすことができる場合があります。

その後、補間分数として0から1までの乱数を選択します。

于 2013-02-15T10:30:16.837 に答える