2017-12-04 8 views
3

2Dグラフィックスの場合、関数を最適化する必要がありますが、SBCLではSBCLが算術演算をインライン化できないというコメントがたくさんあります。私はあらゆる種類の宣言を試みましたが、コンパイラを満足させるようには見えません。簡単な例を次に示します。Common Lisp:SBCLの関数の最適化

(defun test-floor (x div) 
    (declare (type single-float x) 
      (type (signed-byte 64) div) 
      (optimize (speed 3))) 
    (floor x div)) 

以下の4つの注釈を示します。私は床が組み込み関数なので完全に失われています。私はSBCLでコンパイラのヒントを適切に与える方法に関する情報やチュートリアルを見つけようとしましたが、適切な情報を見つけられなかったので、どんな情報も非常に高く評価されます!残念ながら、Common Lispでの最適化は私にとっては非常に未知の領域です。 LinuxマシンでSBCL 1.3.20を使用しています。

; file: /tmp/file595dqU 
; in: defun test-floor 
;  (FLOOR CL-FLOCKS::X CL-FLOCKS::DIV) 
; --> MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL TRUNCATE LET* 
; ==> 
; (SB-KERNEL:%UNARY-TRUNCATE/SINGLE-FLOAT (/ SB-C::X SB-C::F)) 
; 
; note: forced to do full call 
;  unable to do inline float truncate (cost 5) because: 
;  The result is a (values integer &optional), not a (values 
;               (signed-byte 64) &rest 
;               t). 

; --> MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL TRUNCATE LET* VALUES - * 
; ==> 
; (SB-KERNEL:%SINGLE-FLOAT SB-C::RES) 
; 
; note: forced to do full call 
;  unable to do inline float coercion (cost 5) because: 
;  The first argument is a integer, not a (signed-byte 64). 

; --> MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL FUNCTION IF VALUES 1- 
; ==> 
; (- SB-C::TRU 1) 
; 
; note: forced to do generic-- (cost 10) 
;  unable to do inline fixnum arithmetic (cost 1) because: 
;  The first argument is a integer, not a fixnum. 
;  The result is a (values integer &optional), not a (values fixnum &rest t). 
;  unable to do inline fixnum arithmetic (cost 2) because: 
;  The first argument is a integer, not a fixnum. 
;  The result is a (values integer &optional), not a (values fixnum &rest t). 
;  etc. 

; --> MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL FUNCTION IF VALUES 
; ==> 
; (+ REM SB-C::DIVISOR) 
; 
; note: doing signed word to integer coercion (cost 20) from div, for: 
;  the second argument of generic-+ 
; 
; compilation unit finished 
; printed 4 notes 

CL-USER> 

答えて

1

SBCLは、戻り値が64ビット整数に収まるほど小さいものと確信していないので、それはfloorへの呼び出しを最適化することができないことを主張しています。

CL-USER> (test-floor 1f25 1234) 
8103727629894569426944 ; 
0.0 
CL-USER> (format nil “~b” *) 
;; a long binary string 
CL-USER> (length *) 
73 

73ビット整数が返される可能性がありますが、64ビットには適合しません。

また、SeのSBCLマニュアル:

編集:いくつかの検索の後、私はの変換を発見しました。それはhereです。私は以下のそれを再現:

(deftransform floor ((number divisor)) 
    `(multiple-value-bind (tru rem) (truncate number divisor) 
    (if (and (not (zerop rem)) 
       (if (minusp divisor) 
        (plusp number) 
        (minusp number))) 
     (values (1- tru) (+ rem divisor)) 
     (values tru rem)))) 

だから、コンパイラのメッセージは、あなたがfloorを呼び出すと、あなたは数字の異なるサブタイプに対処する必要がおよそ

+0

これは非常に合理的なようです、ありがとう!結果が64ビットの範囲を超えないことをコンパイラに伝える方法はありますか?または、コアダンプの答えに示唆されているようにffloorを使用する方が効率的でしょうか? –

+0

まだ何も見つかりませんでした –

2

を話していたものを説明します。コードは、整数でフロートを分割します(その整数を浮動小数点として強制することを含む)、結果を整数に戻して強制しなければなりません。これは正しく実行されなければならない作業の量であり、入力タイプを制限しないと、バイパスすることはまずありません。

ffloorを代わりに使用する場合、主な結果は浮動小数点です(実際に必要なときに整数に後で丸めることができます(ピクセル座標に変換するなど)。次のコードはコンパイルのメモを与えるものではありません:あなたも発信者に適切な型の値を提供する(とランタイムチェックを実行)の責任をプッシュするだろうfloatを、あるとしてdivを宣言すること

(defun test-floor (x div) 
    (declare (type single-float x) 
      (type fixnum div) 
      (optimize (speed 3))) 
    (ffloor x div)) 

関数を定義する前におそらく(declaim (inline test-floor))にする必要があります。これはコンパイラが入力パラメータのタイプとボクシング結果のチェックを避けるためにコードにショートカットを入れることができるためです。

編集:

フロートの範囲は、(原因指数)が大きい可能な領域をカバーする:より密に無限大に向かってより離間し、ゼロに近い充填します。整数値は直線的に配置されますが、同じ数のビットでより小さな範囲をカバーします。したがって、あなたの出力がfixnumに収まるようにするには、入力のfloatがfixnumの範囲外にならないようにする必要があります。私は、次のことを試してみました:

(defun test-round (x) 
    (declare (type (single-float #.(float most-negative-fixnum 0f0) 
           #.(float (/ most-positive-fixnum 2) 0f0)) x) 
      (optimize (speed 3))) 
    (round x)) 

私はあなたがテストしたときに理由山車の上半分の範囲に持っていた:

(typep (round (coerce most-positive-fixnum 'single-float)) 'fixnum) 

...それはNILを返します。なぜこのようなことが起こるのか分かりませんが、これは実装とアーキテクチャによって異なります。最も正のfixnumの半分を取ることで、値がfixnumとして変換されるのに十分低いことが保証されます。今、私はもう編集メモはありません。

(同じことが(signed-byte 64)のために行く))

NB。上記の例とは異なり、deftypeを使用し、どこでも同じ宣言を繰り返さないようにしてください。

(the fixnum (1+ 3)) 

をしかし、あなたは本当に価値が実際にFIXNUMすることを確認する:あなたは、式の戻り値を指定したい場合は

+0

ありがとう、本当に役に立ちました!あなたが正しく仮定するので、私は多くのピクセル計算を行い、ピクセルは配列に格納されます。しかし、私はまだ、コンパイラの苦情なしで山車を丸めるための方法を発見していない: '(関数定義のテスト・ラウンド(x)の ((型単フロートxを宣言する) (最適化(速度3))) (round x)) ' –

+0

コンパイラは、結果が整数であり、符号付きバイト64ではないことを訴える。実際に値が* 64ビットの範囲内にあることをコンパイラに何とか伝えることができればいいと思います。 –

+0

問題は、結果が64ビットの範囲にあることをどのように知っていますか?これが当てはまらない場合はどうなりますか? SBCLはあなたが間違ったことをするのを防ぐのを本当に頑張っています。結果が期待どおりの範囲にあるように入力floatドメインを制限する必要があります(編集します) – coredump

2

あなたはTHEを使用することができます。あなたが嘘をついているなら、Lispはあなたを信じており、あなたは不特定のランタイム効果を持っています。 SBCLはコンパイル時に警告するかもしれませんが、実際にはそれを処理する必要があります。間違った型が間違った型を返すと、データが破損したり、Lispがクラッシュする可能性があります。機能ithは整数であり、引数としてリストをとることができ、例えば

戻り値を指定する他の方法はFTYPE宣言です。任意の型 - >Tまたは任意のサブタイプを返します。例えば

(declaim (ftype (function (integer list) t) 
       ith)) 

あなたがいることを確認する必要がありあり
(the fixnum (+ (the fixnum a) (the fixnum b))) 

:aはFIXNUM

  • bはFIXNUM
  • との和である

    • bも常にfixnumです

    aとbの合計は間違いなくFIXNUMあるので、ここでは、これは、簡単です:

    CL-USER 3 > (let ((a 3) (b 12)) 
           (the fixnum (+ (the (integer 0 10) a) 
              (the (integer 3 20) b)))) 
    15 
    

    Lispは、実行時にそれを確認し、および/またはコンパイル時のこと。この追加は簡単なfixnum操作となり、fixnumオーバーフローやbignumを処理する必要はありません。 safetyの値を何か低い値に設定すると、ランタイムチェックも省略することができます。しかし、間違った型でこのコードを呼び出すべきではありません。

  • +1

    ありがとう、それもとても便利です!原則的にはっきりしていましたが、コードでどのように指定するのか分からず、このトピックに関する情報を見つけるのは難しいようでした。これらのコメントはすべて、CL/SBCLの最適化をよりよく理解するのに役立ちます。お時間をいただければ幸いです! –