1

スキームを使用すると、次の関数を使用する必要があります。(すべての引数は自然数[0、inf))

(define safe-div
  (lambda (num denom safe)
    (if (zero? denom)
        safe
        (div num denom))))

ただし、この関数は頻繁に呼び出され、十分に機能していません(速度に関して)。目的の動作を実装するためのより効率的な方法はありますか(numとdenomの整数除算、denomがゼロの場合は安全な値を返します)?

注、私はChezスキームを使用していますが、これは完全なChezではなくrnrsのみをインポートするライブラリで使用されています。

4

1 に答える 1

7

最高のパフォーマンスを得るには、シリコンにできるだけ近づける必要があります。このような安全性チェックを追加しても、スキームシステムによって超効率的なマシンコードにジャストインタイムでコンパイルされない限り、それは実行されません。

2つの選択肢があります。1つは、C(またはアセンブリ)でネイティブ(つまり外部)実装を作成し、それを呼び出すことです。これはラムダとしてパッケージ化することと互換性がない可能性がありますが、ラムダの動的な性質は表記上の効率につながりますが、必ずしも実行時の効率にはなりません。(関数ポインターを除いて、ラムダ式がCに存在しないのには、何年も前のものであるにもかかわらず、理由があります。)このルートに進む場合は、一歩下がって、safe-divの処理が大きいかどうかを確認することをお勧めします。一部はネイティブにする必要があります。ループの周りのすべてがまだ遅い場合、ループの中央で分割を高速化する意味はほとんどありません。

ゼロによる除算がまれであると予想される場合、別のアプローチは、単に使用divして、その実装が高速であることを期待することです。はい、これはゼロ除算につながる可能性がありますが、速度に関しては、許可を求めるよりも許しを請う方がよい場合があります。言い換えれば、分割前のチェックをスキップして、それを実行するだけです。失敗した場合、スキームランタイムはゼロ除算の障害をキャッチする必要があり、そのための例外ハンドラーをインストールできます。これにより、例外的な場合はコードが遅くなり、通常の場合はコードが速くなります。うまくいけば、このトレードオフがパフォーマンスの向上につながるでしょう。

最後に、除算する対象によっては、実際の除算を実行するよりも逆数を掛ける方が速い場合があります。これには、高速の逆数計算、または逆数を直接生成するために以前の計算を修正する必要があります。整数を扱っているので、逆数は固定点に格納されます。これは基本的に2 ^ 32 * 1/denomです。これにnumを掛け、右に32ビットシフトして商を取得します。最近ではより多くのプロセッサがシングルサイクルの乗算命令を持っているため、これは成功につながりますが、分割はチップ上でループで実行され、はるかに低速です。これはあなたのニーズにはやり過ぎかもしれませんが、ある時点で役立つかもしれません。

于 2012-04-17T04:42:31.910 に答える