4

(mapcar #'fn ...)最初の質問: Common Lisp 用の最新のコンパイラが通常コンパイルし(map 'list #'fn ...)て と同じコードになると仮定するのは合理的(mapc #'fn ...)ですか? つまり、新しいリストを作成する必要がないように、戻り値が無視されることをコンパイラが認識すると想定するのは合理的でしょうか? たとえば、ソース ファイルに次のコードが含まれているとします。

(defun set-foo-5 (sym)
  (setf (get sym 'foo) 5))

(progn 
  (mapcar #'set-foo-5 '(a b c))
  (format t "All foos are five!~%"))

mapcより効率的でしょうか?私は通常 SBCL を実行しますが、優れたコンパイラであれば、この状況では新しいリストを作成する必要がないことを理解できると思います。私は正しいですか?

2 番目の質問: 同じ状況で、最新のコンパイラは、ソース コードに存在し、実行時に選択されない限り、通常map 'listは と同じコードにコンパイルされると想定する必要がありますか?mapcar'list

3 番目の質問: 他のシーケンスについても同様の質問です。たとえば、mapcar上記の progn の行を に置き換えた場合(map 'vector #'set-foo-5 #(a b c))、コンパイルされたコードはわざわざ新しいベクトルを作成しないと想定する必要がありますか?

4

2 に答える 2

10

まず第一に、mapmapcarは非常に重要な点で異なります: 後者はリストで動作しますが、前者は任意のシーケンスで動作します。これは、すべてのデータ引数がsであることをコンパイラが証明(map nil ...)できる場合にのみ、 (or ) が(resp. )(map 'list ...)と同等であることを意味します。(mapc ...)(mapcar ...)list

第二に、ほとんどの最新の Lisp コンパイラ (たとえばSBCL ) は通常、そのようなことを (必要に応じて宣言を使用して) 理解するのに十分です。

第三に、確実にする唯一の方法は、 を使用することdisassembleです。

第 4に、関数の選択はコードを文書化する方法です。を使用するmapと、コードを読む人間に、非リストを関数に渡すことを伝えます。リストのみが使用されると確信している場合、なぜ読者 (数か月後の自分自身) を混乱させたいのでしょうか?

第 5に、時期尚早の最適化は諸悪の根源です

于 2013-09-29T00:56:47.810 に答える
3

@sds の回答のいくつかのヒントを使用して、特定の実装に関する私の質問に答える可能性のある簡単な予備テストがあることに気付きました (dissemble出力を処理する必要はありません)。SBCL と CCL は、の戻り値が無視される場合mapcarと同じように処理するとは限らないようです。最初に と を有意な長さのリストとして定義し(私のものは長さが 100,000 で、整数が含まれていました)、次に、次のようにそれらに対して 、 などを何度も実行します (古いガベージをクリアするためのオプションの予備呼び出しを使用)。mapcmapcarlis1lis2mapcarmapcgc

(gc :full t)
(time 
  (progn
    (dotimes (ignored 1000)
      (mapcar #'+ lis1 lis2))
    (format t "mapcar:~%")))

(gc :full t)
(time 
  (progn
    (dotimes (ignored 1000)
      (mapc #'+ lis1 lis2))
    (format t "mapc:~%")))

(gc :full t)
(time 
  (progn
    (dotimes (ignored 1000)
      (map nil #'+ (the list lis1) (the list lis2)))
    (format t "map nil with lists~%")))

たとえば、私のマシンで SBCL を実行すると、次のようになります。

mapcar:
Evaluation took:
  2.306 seconds of real time
  2.287627 seconds of total run time (2.136130 user, 0.151497 system)
  [ Run times consist of 0.147 seconds GC time, and 2.141 seconds non-GC time. ]
  99.22% CPU
  3,683,188,504 processor cycles
  1,600,049,536 bytes consed

mapc:
Evaluation took:
  0.639 seconds of real time
  0.638733 seconds of total run time (0.638011 user, 0.000722 system)
  100.00% CPU
  1,020,310,296 processor cycles
  0 bytes consed

map nil with lists
Evaluation took:
  0.592 seconds of real time
  0.592114 seconds of total run time (0.591199 user, 0.000915 system)
  100.00% CPU
  945,957,944 processor cycles
  0 bytes consed

これらは、デフォルトの最適化設定での典型的な結果です。速度、非安全性などを最適化するためにを使用declaimすると、少し速度が上がりますが、とmapcmap nilよりも桁違いに速いという事実は変わりません。CCL の結果も同様ですが、全体的に遅くなります。mapcarmapcar

于 2013-09-29T06:38:21.653 に答える