7

以下のプログラムは非常に非効率的です。SBCL 1.0.53 では 6.361 秒の非 GC 時間とは対照的に、GC 時間は 28.980 秒かかります。

(deftype vec3 () '(simple-array double-float (3)))

(declaim (inline make-vec3 vec3-zero
             vec3-x vec3-y vec3-z
             vec3-+))

(defun make-vec3 (x y z)
  (declare (optimize (speed 3) (safety 0)))
  (make-array 3 :element-type 'double-float
                :initial-contents (list x y z)))

(defun vec3-zero ()
  (make-vec3 0.0d0 0.0d0 0.0d0))

(defun vec3-x (x)
  (declare (optimize (speed 3) (safety 0)))
  (declare (type (simple-array double-float (3)) x))
  (aref x 0))

(defun vec3-y (x)
  (declare (optimize (speed 3) (safety 0)))
  (declare (type (simple-array double-float (3)) x))
  (aref x 1))

(defun vec3-z (x)
  (declare (optimize (speed 3) (safety 0)))
  (declare (type (simple-array double-float (3)) x))
  (aref x 2))

(defun vec3-+ (a b)
  (declare (optimize (speed 3) (safety 0)))
  (make-vec3 (+ (vec3-x a) (vec3-x b))
             (+ (vec3-y a) (vec3-y b))
             (+ (vec3-z a) (vec3-z b))))


;; main

(defun image (x y)
  (make-array (* x y) :element-type 'vec3 :initial-element (vec3-zero)))

(defun add (to from val)
  (declare (type (simple-array vec3 (*)) to from)
           (type vec3 val)
           (optimize (speed 3) (safety 0)))
  (let ((size (array-dimension to 0)))
    (dotimes (i size)
      (setf (aref to i) (vec3-+ (aref from i) val)))))

(defun main ()
  (let ((to (image 800 800))
        (x (make-vec3 1.0d0 1.0d0 1.0d0)))
    (time (dotimes (i 200)
            (add to to x)))
    (print (aref to 0))))

時間:

* (main)
Evaluation took:
  39.530 seconds of real time
  35.340237 seconds of total run time (25.945526 user, 9.394711 system)
  [ Run times consist of 28.980 seconds GC time, and 6.361 seconds non-GC time. ]
  89.40% CPU
  83,778,297,762 processor cycles
  46 page faults
  6,144,014,656 bytes consed


#(200.0d0 200.0d0 200.0d0) 
#(200.0d0 200.0d0 200.0d0)

vec3 の抽象化を維持しながら、より効率的な方法で計算する方法はありますか?

たとえば、マクロを使用して Worker/Wrapper 変換を実装すると、vec3 conses を排除できます。

別の方法として、vec3 のコンス プールを作成すると、メモリ割り当てが減少します。

理想的には、SBCL が vec3 のような一部のデータ構造の非記述子表現を配列要素としてサポートするとよいでしょう。

4

1 に答える 1

5

このような状況では、マクロを利用することをお勧めします。次に、私は常に宣言することを躊躇します(安全性0)。これは非常にわずかなパフォーマンスの向上をもたらし、defun内のコードだけでなく、defunを呼び出すすべてのコードも完全に正しくない場合、奇妙な動作を引き起こす可能性があります。

ここで重要なことは、make-vec3で新しいリストオブジェクトを作成しないことです。私はあなたのコードのやや迅速で汚い最適化を添付します。私のマシンでは、元のコードはで実行されます

; cpu time (non-gc) 27.487818 sec user, 0.008999 sec system
; cpu time (gc)     17.334368 sec user, 0.001999 sec system
; cpu time (total)  44.822186 sec user, 0.010998 sec system
; real time  44.839858 sec
; space allocation:
;  0 cons cells, 45,056,000,000 other bytes, 0 static bytes

そして私のバージョンはで実行されます

; cpu time (non-gc) 4.075385 sec user, 0.001000 sec system
; cpu time (gc)     2.162666 sec user, 0.000000 sec system
; cpu time (total)  6.238051 sec user, 0.001000 sec system
; real time  6.240055 sec
; space allocation:
;  8 cons cells, 8,192,030,976 other bytes, 0 static bytes

これはAllegroを使用しています。他のLispのYMMV。あなたはvec3配列のconses/memoryをプールすることについて言及しました、そして私はそれらのオブジェクトを再利用すること、すなわちそれらを破壊的に変更することはあなたがそうする機会があるとき良い考えだと思います。私のLispでは、vec3は64バイトかかりますが、これはかなりの量です...もう1つの便利なことは、もちろんプロファイラーを呼び出して、どこで時間が費やされているかを確認することです。また、これらの数学が多い問題では、配列参照と算術演算を可能な限りオープンコード化することが重要です。ほとんどのlispは(my-functionを分解する)ことができます。これは、これらの操作が実際にオープンコード化されているかどうか、またはランタイムが呼び出されているかどうかを示します。

(deftype vec3 () '(simple-array double-float (3)))

(declaim (optimize (speed 3) (debug 0) (safety 1)))

(defmacro make-vec3 (x y z)
  `(let ((vec3 
     (make-array 3 :element-type 'double-float :initial-element 0.0d0)))
   (setf (aref vec3 0) ,x
         (aref vec3 1) ,y
         (aref vec3 2) ,z)
     vec3))


(defun vec3-zero ()
  (make-vec3 0.0d0 0.0d0 0.0d0))

(defmacro vec3-x (x)
  `(aref ,x 0))

(defmacro vec3-y (x)
  `(aref ,x 1))

(defmacro vec3-z (x)
  `(aref ,x 2))

(defun vec3-+ (a b)
  (declare (type vec3 a b))
  (make-vec3 (+ (vec3-x a) (vec3-x b))
             (+ (vec3-y a) (vec3-y b))
             (+ (vec3-z a) (vec3-z b))))

(defun image (x y)
  (make-array (* x y) :element-type 'vec3 :initial-element (vec3-zero)))

(defun add (to from val)
  (declare (type (simple-array vec3 (*)) to from)
           (type vec3 val))
  (let ((size (array-dimension to 0)))
    (dotimes (i size)
      (setf (aref to i) (vec3-+ (aref from i) val)))))

(defun main ()
  (let ((to (image 800 800))
        (x (make-vec3 1.0d0 1.0d0 1.0d0)))
    (time (dotimes (i 200)
            (add to to x)))
    (print (aref to 0))))
于 2011-12-02T19:00:57.833 に答える