2012-02-14 16 views
24

私たちはCFDソルバーを持っていて、シミュレーションを実行している間にマシンによっては非常に遅く実行されていましたが、非常に遅いpow()関数の置き換え

RHOV= RHO_INF*((1.0_wp - COEFF*EXP(F0)))**(1.0_wp/(GAMM - 1.0_wp)) 

ドリルのVTuneで、問題はcall pow組立ラインにトレースし、スタックをトレースするとき、それが使用したことを示した:インテルのVTuneを使用して、次の行が(Fortranで)問題で見出されました__slowpow()。何らかの検索の後、this pageが同じことについて不平を言った。

libcバージョン2.12のマシンでは、シミュレーションに18秒かかりました。 libcバージョン2.14のマシンでは、シミュレーションには0秒かかりました。

上記のページの情報に基づいて、pow()の底辺が1.0に近い場合に問題が発生します。だから我々はpow()の前に任意の数で基数をスケールし、次にpow()呼び出しの後に指数に上げた数で除算した別の簡単なテストを行った。これにより、libc 2.12でもランタイムが18秒から0秒に落ちました。

ただし、コード全体にこれを置くことは現実的ではありません。a**b libcのpow()機能をどのように置き換えるのですか?例えば、アセンブリラインcall powがFortranコンパイラによって生成され、カスタムpow()という関数を呼び出してスケーリングを行い、libc pow()を呼び出し、スケーリングで除算したいとします。コンパイラに対して透過的な中間層を作成するにはどうすればよいですか?

は、我々は(擬似コード)のようなものを探している明確にする編集:

double pow(a,b) { 
    a *= 5.0 
    tmp = pow_from_libc(a,b) 
    return tmp/pow_from_libc(5.0, b) 
} 

はそれにlibcからpowをロードして、私たちのカスタム関数でその名前を変更することは可能です命名の競合を避けるために? customPow.oファイルの名前をpowからlibcに変更できる場合は、libcがまだ他のものに必要な場合はどうなりますか? libcのpowcustomPow.opowの名前の競合が発生しますか?

+0

Good ol 'Fortran!興味深い質問+1 –

答えて

7

だけで、あなた自身のpow関数を記述し、どこかリンカのライブラリパス内の静的ライブラリアーカイブlibmypow.a.oファイルを置いて、リンク時に-lmypowを渡します。

+1

カスタム 'pow'関数しかし、libcで 'pow'を呼び出すことはできますか?このカスタム 'pow'は、必要に応じてベースを拡大/縮小し、libc' pow'を呼び出し、必要に応じてスケールを解除します。いくつかの名前の競合があるようです。 – tpg2114

+9

ダイナミックリンクを使用している場合、目的の動作を達成するために使用できる 'dlsym'ハックがありますが、脆弱です。より良いアプローチは、GNUリンカーを使ってシステム上で動作する必要がある場合、 'ld'に対する' --wrap'オプションです( 'gcc'は' -Wl、-wrap、 )。次に、 'libmypow.a'に' __wrap_pow'を入れ、li​​bc powを使う必要があるところで '__real_pow'を呼び出させてください。すべてがうまくいくはずです。 –

3

pow(a,b)exp(b*ln(a))と同じですが、おそらくその置換はあなたのために機能します。

+1

これは、呼び出しの遅さを回避する可能性がありますが、実際のコードベースを変更することなくFortranの '**'演算子によって生成された関数呼び出しを本質的に置き換える方法を探しています。可能なら。 – tpg2114

+2

このIDを使用する独自のバージョンのpow()をリンクします。 –

+2

これは 'call pow'で1.0000000000000020^1.5:1.0000000000000031、' -ffast-math'で1.0000000000000029、exp(b * ln(a))で1.5000000000000013と異なる結果をもたらします。 – steabert

1

私はこれを自分でテストしましたが、リンク先のページからテストプログラムをコンパイルすると、アセンブリコードでcall powが使用されます。ただし、最適化を使用してコンパイルすると、powへの呼び出しはありませんが、結果は若干異なります。

22

さて、今すぐお待ちください。図書館はあなたとおもちゃにちょうど__slowpow()を呼んでいません。 __slowpow()と呼んでいます。なぜなら、与えている値に対して正確な結果を与えるには余分な精度が必要だと考えているからです(この場合は、基数1に非常に近く、指数1の指数です)。この計算の正確さを気にしているなら、その理由を理解し、それを回避しようとする前にそれが重要かどうかを理解する必要があります。大きな正のF0に対して、この全体を安全に丸めて1にすることができます。それは後でこの値で何が行われたかによって異なります。1.d0からこの結果を差し引いた値が必要な場合は、余分な精度が必要になります。

+0

それは確かに本当です。しかし、私たちのコードでは、少なくとも私たちのコードは次元的なものであるため、近くに拠点を置く唯一の時間は、視覚化や後処理のためのものをコンピューティングするときです。したがって、正解の1〜15以内に入ることは非常に重要ではありません。私はどれくらい失うかを比較するために比較を実行しました。誤差は〜1e-13です.2次の正確なコードは離散化誤差よりも小さいので、すべてのpow()をやや精度の低いもの。 – tpg2114