LISPUSER

SBCL - Steel Bank Common LispLisp isn't a language, it's a building material.

(Top Page) (Lisp Memo)

公式ページhttp://www.sbcl.org/
最新バージョン1.0.28

翻訳情報

SBCL について (英語原文: http://www.sbcl.org/)

SBCL はオープンソース(フリーソフトウェア)な ANSI Common Lisp のコンパ イラとランタイムです.ネイティブコンパイラ,デバッガ,その他の拡張が統 合された対話的環境を提供します.

SBCL は多くのプラットフォーム上で動作します.現在サポートされているプラッ トフォームやどのように新しいプラットフォームに SBCL を移植するかについ てより詳細な情報については プラットフォームサポートページ を参照して ください.

SBCL の最新バージョンは 2007/6/26 にリリースされた 1.0.7 です.リリース ノートは ニュース ページで参照できます. お使いのプラットフォーム向 けの SBCL 配布物をダウンロードするには SBCL の入手方法 ページを御覧 ください.

ドキュメント

SBCL の TeXInfo 形式のマニュアルは現在執筆中です.最新の TeXInfo ソース はソースコードに含まれている doc/manual ディレクトリに含まれています.

その他

内部実装についての詳細に興味のある人は SBCL Internals という CLiki から読んでゆくと良いでしょう.0.9.9 から Win32 対応パッチのマージが開始 されたので,今後は Windows でも SBCL が使えるようになるのかもしれません ね.現在でも SBCL のビルド程度は可能ですが,添付のモジュール等の対応具 合もあり趣味人以外が使える段階ではないです.

0.9.17 から、MSI 形式のインストーラが用意されて、インストールが楽になり つつあります。まぁ、Win32 上ではスレッドも使えないうえ、 Win32 サポート はほとんどないので、CLISP に比べてあまりメリットがありませんが、一応 SLIME 環境も構築できています。イメージのサイズが大きすぎて未だに PHS 回 線しかない私には配布できません。

http://prdownloads.sourceforge.net/sbcl/sbcl-0.9.17-x86-windows-binary.msi?download

日本語コンテンツ

1.0.7 -> 1.0.8 での変更点 (予定)

  • enhancement: experimental function SB-EXT:RESTRICT-COMPILER-POLICY allows assining a global minimum value to optimization qualities (overriding proclamations and declarations).
  • enhancement: closed over variables can be stack-allocated on x86 and x86-64.
  • performance bug fix: GETHASH and (SETF GETHASH) are once again non-consing.
  • optimization: STRING-TO-OCTETS is now up to 60% faster for UTF-8.
  • bug fix: using obsoleted structure instances with TYPEP and generic functions now signals a sensible error.
  • bug fix: threads waiting on GET-FOREGROUND can be interrupted. (reported by Kristoffer Kvello)
  • bug fix: backtrace construction is now more careful when making lisp-objects from pointers on the stack, to avoid creating bogus objects that can be seen by the GC.
  • bug fix: defaulting of values in contexts expecting more than 7 variables now works on x86-64. (reported by Christopher Laux)

1.0.6 -> 1.0.7 での変更点

  • MOP の改良: SB-MOP:SPECIALIZER のユーザー定義サブクラスサポートが改良されました。 実験的なインターフェース関数 SB-PCL:MAKE-METHOD-SPECIALIZERS-FORM が DEFMETHOD 展開の一部として呼ばれるようになり、 評価された時に特定子 (specializer) リストを生成するフォームを生成します。 SB-PCL:[UN]PARSE-SPECIALIZER-USING-CLASS はデバッグやイントロスペクトのサポートを提供します。
  • マイナーな非互換変更: 非サポートだった spinlock のインターフェースが変更になりました。 フリーなスピンロックは NIL の値を持ち、閉じたスピンロックはロックを取得しているスレッドそのものを値として保持します。
  • 改善: WITHOUT-INTERRUPTS は ALLOW-WITH-INTERRUPTS と WITH-LOCAL-INTERRUPTS をローカルマクロとして束縛するようになりました。詳細は docstring を参照してください
  • 改善: ソケットストリームの名前が "a constant string" にかわって "a socket" になりました
  • 改善: SB-POSIX が lockf() をサポートするようになりました (Thanks to Zach Beane.)
  • 改善: SB-POSIX が getcwd() をサポートするようになりました (Thanks to Tassilo Horn.)
  • 改善: ジェネリック関数に対する SB-INTROSPECT:FUNCTION-ARGLIST の引数表示が改良されました (Thanks to Tobias C. Rittweiler)
  • 最適化: bignum の表示速度が 20-40% 高速化しました (bignum のサイズによります)
  • バグ修正: WITH-MUTEX や WITH-RECURSIVE-LOCK は Linux においてインタラプトセーフになりました
  • バグ修正: メソッドやスロットのオフセット、定数値などを保持する CLOS キャッシュがスレッド/インタラプトセーフになりました
  • バグ修正: ジェネリック関数のディスパッチ関数更新がスレッド/インタラプトセーフになりました
  • バグ修正: ADD/REMOVE-METHOD がスレッド/インタラプトセーフになりました
  • バグ修正: EQL-specializers のインターンがスレッド/インタラプトセーフになりました
  • バグ修正: SB-POSIX あるいは SB-BSD-SOCKETS に依存した ASDF システムが :FORCE T 指定でロード可能になりました
  • バグ修正: 適用メソッドの計算処理の割り込みに対する安全性が向上しました

1.0.5 -> 1.0.6 での変更点

  • new contrib: 実験的なコードカバレッジツール sb-cover モジュールが含まれるようになりました
  • 最適化: 1 バイトエンコーディングと UTF-8 に対する STRING-TO-OCTETS が大幅に高速化しました
  • 最適化: (AREF FOO (+ INDEX <constant>)) 形式の呼び出しは、もしコンパイラが (+ INDEX <constant>) の境界チェックと FOO の要素が少なくとも 8bit の幅を持っている事を特定できる場合に、x86-64 プラットフォームにおいてより効率のよいコードを生成するようになりました
  • 改善: 新しい実験的な同期タイムアウトが提供されるようになりました。詳細は SB-SYS:WITH-DEADLINE を参照してください
  • 改善: シンボル名の衝突がエラーを発生させたとき、衝突したシンボルが常にパッケージプリフィックス付きで表示されるようになりました (thanks to Kevin Reid)
  • 改善: ステップ実行が SPARC や MIPS プラットフォーム上で再びサポートされるようになりました (It is also now more likely to work on CheneyGC builds on the PPC.)
  • 改善: sb-sprof は正確な呼び出し回数を追跡・報告するようになりました
  • バグ修正: SB-MOP:SPECIALIZER の非標準なサブクラスの扱いがより正確になりました
  • 非互換な変更: 世代別ガベージコレクタを使っているプラットフォームにおいて PURIFY はもはやダイナミック領域のデータを静的領域やリードオンリー領域へとコピーすることはありません
  • バグ修正: GETHASH, (SETF GETHASH), CLRHASH や REMHASH はインタラプトセーフになりました
  • バグ修正: 競合状態によって、ときどき GC が "SIGSTOPFORGC blocked at a bad place" というメッセージとともにクラッシュするバグが修正されました
  • バグ修正: マクロのラムリスト中の &ENVIRONMENT 変数は無視するように宣言するとこが可能となりました
  • バグ修正: DEFSETF のラムダリストが &ENVIRONMENT を含んでいない場合に STYLE-WARNING が通知されないようになりました (regression from 1.0.4.)
  • バグ修正: システムが GC を抑止した状態で動作していても非同期割り込みが可能となりました
  • バグ修正: DECLARE フォームがトップレベルで評価された場合にただ NIL を返すのではなく、エラーとなるようになりました
  • バグ修正: DEBUG を高レベルに設定した状態でコンパイルされた関数をディスアセンブルしようとした場合にフォームのルックアップエラーが通知されていたのが修正されました (reported by Peter Graves)

1.0.4 -> 1.0.5 での変更点

  • 非互換な変更: host-ent-name, host-ent-addresses に対する writer メソッドが 削除されました – 値を変更しても DNS の情報は更新されないから
  • マイナーな非互換変更: JOIN-THREAD インターフェースが試験的に変更されました
  • ドキュメンテーション: マニュアルのクラススロットのセクションに reader/writer メソッドが載りました (Richard M Kreuter に感謝)
  • ドキュメンテーション: 非同期イベントからの unwind が安全でない事がドキュメント化されました
  • ドキュメンテーション: SB-SYS:WITHOUT-GCING はマルチスレッドアプリケーションにおいては安全でない旨がドキュメント化されました
  • 最適化: GET-INTERNAL-REAL-TIME が POSIX プラットフォームにおいて最適化されました (最適化のヒントをくれた James Anderson に感謝)
  • 最適化: REPLACE, SUBSEQ, COPY-SEQ はより多くのケースで最適化されるようになりました
  • 最適化: STRING-{EQUAL,LESSP,GREATER-P} やその NOT 変種 (STRING-NOT-EQUAL など) が cons セルを消費しないようになりました
  • 最適化: CHAR-{EQUAL,LESSP,GREATERP} やその NOT 変種の直接呼び出しが cons セルを消費しないようになりました
  • 最適化: EQUAL ハッシュテーブルが SXHASH を全てのデータ型に対して使用することはなくなりました。EQUAL == EQL な型に関しては、かわりに EQL ハッシュを使います
  • 最適化: インライン化されていない AREF や (SETF AREF) が大きく高速化しました
  • 最適化: 新しい STRING-OUTPUT-STREAM の実装はサイズの大きい出力に対して高速化し、平均で 30% ほど消費するコンスセルの量が低減しました
  • 改良: XREF 情報が SYMBOL-VALUE や定数によって作られたグローバル変数の情報も収集するようになりました
  • 改良: SIGINT は SB-SYS:INTERACTIVE-INTERRUPT コンディションを通知するようになりました
  • バグ修正: FILE-POSITION は ANSI 規格が NIL を返す事を要求しているケースでエラーを通知することがありました
  • バグ修正: ADJUST-ARRAY が interrupt-safe になりました
  • バグ修正: fd-handler の追加/削除が interrupt-safe になりました
  • バグ修正: C へのインライン呼び出しが x86/Darwin 上で 16 バイトスタックアラインメントを保証するようになりました
  • バグ修正: CLOS 実装内の不適切な型宣言が修正されました (James Anderson の報告によるものです)
  • バグ修正: x86-64 上で ROOM が不正なレポートを返すバグが修正されました (Lutz Euler に感謝)
  • バグ修正: CLHS の要求に沿って DEFSETF は &ENVIRONMENT を受けつけるようになり、また &AUX を受けつけなくなりました. (Samium Gromoff の報告によるものです)
  • バグ修正: 未束縛の変数への参照はエラーを通知するようになりました (Marco Monteiro の報告によるものです)
  • バグ修正: 未使用の値を使った / はセーフコード内では削除されないようになりました (Marco Monteiro と Kevin Reid に感謝)
  • バグ修正: ファイル内の一行に書き込める文字数が無制限になりました
  • バグ修正: 非同期割り込みによって生じていたいくつかの GC のデッドロックが GC の割り込みを抑制することによって修正されました
  • バグ修正: GETHASH, (SETF GETHASH), CLRHASH, REMHASH におけるいくつかの割り込みの問題が修正されました
  • バグ修正: *BREAK-ON-SIGNALS* の束縛する値が型指定子でなくてももはや無限再帰が生じないようになりました
  • バグ修正: SB-EXT:MUFFLE-CONDITIONS 宣言は DEFMETHOD 本体内での不要な警告を引き起こさないようになりました (Kevin Reid の報告によるものです)
  • バグ修正: 正しい変数情報の &environment 引数がバイトコンパイルの間に展開されるマクロに渡されるようになりました (Samium Gromoff の報告によるものです)
  • バグ修正: bignum に対するハッシュ関数が過度の衝突を起こす論理エラーが修正されました (Faré Rideau の報告によるものです)
  • バグ修正: 配列要素の変更は配列の SXHASH 値の変更を生じさせていましたが、これは文字列とビットベクタにのみ許される挙動しでした (0.9.16 で導入されたバグです)
  • 改良: x86-64/darwin 移植版ですべてのテストが通るようになりました (デバッガのテストは除きます) しかし、残課題の修正が終るまでは実験的な版だとすべきです。
  • 改良: 重複キーをもった CASE 節がスタイル警告を通知するようになりました (Kevin Reid に感謝)
  • 改良: macos/x86 と macos/x86-64 で不正なインストラクションやメモリ保護違反を例外ハンドラで捉えられるようになりました (エラー処理や GC の開発に有効です).

1.0.3 -> 1.0.4 での変更点

  • 新しいプラットフォーム: x86-64/darwin (MacOS) の実験的サポート
  • 非互換な変更: 利用可能な場合には gethostbyaddr, gethostbyname にかわって、ほとんどのプラットフォームでスレッドセーフな getaddrinfo や getnameinfo などのソケット関数を使うように変更されました。結果として、それらのプラットフォームでは HOST-ENT の ALIASES フィールドは常に NIL を返すようになります。
  • 変更: コアを埋め込んだランタイム (たとえば :EXECUTABLE T で保存されたもの) は起動時にバナー文字列を表示しないようになりました。これはちょうどコマンドライン引数で --noinform が渡された場合と同じ動作です。(thanks to Kevin Reid)
  • 新しい実験的機能: JOIN-THREAD の追加 (by NIIMI Satoshi)
  • 最適化: 型指定なしの alien value (FFI で使う値)を用いるコードが高速化されました
  • 最適化: コンパイラは、より多くの場合に SEARCH 関数を展開するようになりました
  • 最適化: x86-64 上ので型チェックがよりコンパクトなコードになりました (thanks to Lutz Euler)
  • バグフィックス: 標準化されている COMMON-LISP のスペシャル変数を loop 用変数として利用しても、余計なパッケージロックの警告がでなくなりました (reported by Eric Marsden)
  • バグフィックス: (SINGLE-FLOAT 1.0 2.0) のように範囲指定されたローカルなループ変数の宣言がコンパイル時エラーにならないようになりました (reported by Andras Simon)
  • バグフィックス: >= と <= が NaN に対して誤った結果を返していました (x86-64 ではまだいくつか NaN 関連のバグが残っています)
  • バグフィックス: #= や ## リーダーマクロが呼び出し可能なインスタンスとも正しく組み合わせられるようになりました
  • バグフィックス: 関数の引数に対する型チェックが誤ったレキシカル環境のコンパイラポリシー設定を使ってコンパイルされていた (1.0.2, 1.0.3)
  • バグフィックス: 規格で要求されているとおり SHADOW は文字修飾子として文字列を受け取るようになった (thanks to Eric Marsden)
  • バグフィックス: 非ベース文字列 (no-base string) を引数として外部関数 (foreign function) を呼び出した場合の GC の安全性に関する問題が修正されました
  • バグフィックス: リーダエラーのような致命的なコンパイラの警告に関するより一貫したエラー出力
  • バグフィックス: バックトレース内で関数が正確な名前でな NIL と表示されてしまう (reported by Edi Weitz, regression in 1.0.2)

1.0.2 -> 1.0.3 での変更点

  • 新しく NetBSD/PPC プラットフォームがサポートされました (thanks to Aymeric Vincent)
  • 最適化: (AREF FOO (+ INDEX <constant>)) 形式の呼び出しは、もしコンパイラが (+ INDEX <constant>) の境界チェックが不要で、かつ要素が少なくとも 8bit の幅を 持っていることを決定できる場合に、x86 上でより効率的なコードを生成するようになりました。
  • バグフィックス: 未定義変数への参照は、通常コンパイラにおけるトップレベルフォームと 同様の扱いをされるようになりました。
  • バグフィックス: ビルド用スクリプトが bash でない /bin/sh で再び動作するようなりました。(thanks to Magnus Henoch)
  • バグフィックス: NetBSD 上で asdf-install の TAR-PROGRAM として "gtar" を使うようになりました。 (thanks to Jon Buller)
  • 改善: 複雑な関数のコンパイルがより高速になりました
  • 改善: SB-POSIX に readlink サポートが追加されました (thanks to Richard M Kreuter)

設定ファイル

関連設定ファイルあれこれ.環境は Gentoo Linux + SBCL CVS 版 9.17. です.

.emacs の SBCL 固有部分 – .emacs

:(defun sbcl-start ()

 (interactive)
 (shell-command "sbcl --core /home/onjo/local/lib/sbcl/main.core --load /home/onjo/.slime.lisp &"))

SLIME 用設定ファイル – .slime.lisp

(setq swank::*coding-system* "utf-8-unix") (swank:create-server :port 4005 :dont-close t))

作業用イメージ作成シェルスクリプト – make-sbcl-image

#!/bin/sh

LANG=C sbcl --noinform --no-print <<EOT
(eval-when (:load-toplevel :execute)
  (require :asdf)
  (require :asdf-install)
  (require :sb-rotate-byte)
  (require :sb-md5)
  (require :sb-posix)
  (require :sb-rt)
  (require :sb-simple-streams)
  (require :sb-bsd-sockets)
  ;; (require :sb-aclrepl)
)

(pushnew
  (merge-pathnames "lisp/asd/" (user-homedir-pathname))
  asdf::*central-registry*)

(asdf:oos 'asdf:load-op :cl-fad)
(asdf:oos 'asdf:load-op :cl-interpol)
(asdf:oos 'asdf:load-op :cl-ppcre)
(asdf:oos 'asdf:load-op :cl-who)
(asdf:oos 'asdf:load-op :html-template)
(asdf:oos 'asdf:load-op :iterate)
(asdf:oos 'asdf:load-op :unification)
(asdf:oos 'asdf:load-op :cl-dot)
#-sbcl (asdf:oos 'asdf:load-op :md5)
(asdf:oos 'asdf:load-op :xmls)
(asdf:oos 'asdf:load-op :s-xml)
(asdf:oos 'asdf:load-op :s-xml-rpc)
(asdf:oos 'asdf:load-op :cffi)
(asdf:oos 'asdf:load-op :cffi-uffi-compat)
(asdf:oos 'asdf:load-op :yacc)
(asdf:oos 'asdf:load-op :kmrcl)
(asdf:oos 'asdf:load-op :cl-plus)
(asdf:oos 'asdf:load-op :cl-utilities)
(asdf:oos 'asdf:load-op :json)
(asdf:oos 'asdf:load-op :fiveam)
(asdf:oos 'asdf:load-op :screamer)
(asdf:oos 'asdf:load-op :log4cl)
(asdf:oos 'asdf:load-op :series)
(asdf:oos 'asdf:load-op :ironclad)
(asdf:oos 'asdf:load-op :swank)

#+nil
(progn
  (asdf:oos 'asdf:load-op :uffi)
  (asdf:oos 'asdf:load-op :clsql)
  (asdf:oos 'asdf:load-op :clsql-uffi)
  (asdf:oos 'asdf:load-op :clsql-sqlite3)
  (asdf:oos 'asdf:load-op :curl)

  (asdf:oos 'asdf:load-op :puri)
  (asdf:oos 'asdf:load-op :acl-compat)
  (asdf:oos 'asdf:load-op :htmlgen)
  (asdf:oos 'asdf:load-op :aserve)
  (asdf:oos 'asdf:load-op :webactions))

(asdf:oos 'asdf:load-op :lw-compat)
(asdf:oos 'asdf:load-op :closer-mop)
(asdf:oos 'asdf:load-op :contextl)
(asdf:oos 'asdf:load-op :cldoc)
(asdf:oos 'asdf:load-op :html-encode)
(asdf:oos 'asdf:load-op :colorize)
(asdf:oos 'asdf:load-op :cxml)
;; #+clisp (asdf:oos 'asdf:load-op :clisp-sqlite)
(asdf:oos 'asdf:load-op :rt)

(asdf:oos 'asdf:load-op :clsql)
(asdf:oos 'asdf:load-op :clsql-uffi)
(clsql-sys::push-library-path #p"/usr/lib/")
(asdf:oos 'asdf:load-op :clsql-sqlite3)

(asdf:oos 'asdf:load-op :iso8601-date)
(asdf:oos 'asdf:load-op :local-time)
(asdf:oos 'asdf:load-op :cl-store)
(asdf:oos 'asdf:load-op :cl-prevalence)
;;(load (merge-pathnames "lisp/unstable/lisa/install.lisp" (user-homedir-pathname)))

;; (asdf:oos 'asdf:load-op :lisa)
(load (merge-pathnames "lisp/lib/common/XML/Load-XMLisp.lisp" (user-homedir-pathname)))

(loop repeat 10 do (gc :full t))
(let ((core (merge-pathnames "main.core"
                             (or (sb-posix:getenv "SBCL_HOME") "./"))))
  (save-lisp-and-die core :purify t))
(exit)
EOT

SBCL の新しい XREF 機能について

1.0.1 から入った新しい XREF 機能は、SLIME のサポートも同時に入ったため便利です。なので関連ドキュメントの翻訳。

xref memo (Juho Snellman's Blog の翻訳)

Juho Snellman's Webblog (http://jsnell.iki.fi/blog/archive/2006-12-05-sbcl-xref.html) の翻訳

SBCL は 1.0.0.18 から正式な xref 実装を備えるようになりました。これは「この関数はどこから呼び出されているのか?」というような質問への答えを 与えてくれます。これは M-x slime-list-callers で使われているヒープを調べる方法(これ自体はとても賢いやり方だと思います)よりも、さらに 便利なものです。xref では SLIME のアプローチと比較して以下の利点が得られるように設計しました。

  • より多くの情報: who-macroexpands, who-binds といった who-calls 以外の追加機能
  • より利用しやすく: SLIME はただのトップレベルフォームではなく、直接そのフォームへと貴方をつれていってくれるでしょう
  • より正確に: ヒープ探索ではインライン関数などの扱いは完全に失敗します(また、以下の理由よりにさらに不正確にです)
  • より信頼できるように: ヒープ探索はまた、SBCL 内部のアサーションを引き起す切っかけとなります
  • より高速に: ルックアップはオーダー単位で高速化されます

いくつかの場合では xref の情報は保存されません。経験則に従うなら、 defun と defmethod が同じレベルにあるなら、xref は動作するでしょう。 そうでなければ動作しないでしょう。後者の実例は次のような場合です。

トップレベルでのマクロ展開

:(defmacro def-foo (…) …)

(def-foo ...)
名前付き関数の内側にない lambda に含まれるコード (slime-list-callers は BAR への呼び出しを表示するでしょうが、 slime-who-calls はそうではありません)

:(defvar a

(lambda () (bar)))
defclass の初期化フォーム内のコード

:(defclass ()

 ((a :initform (bar))))
macrolet 定義 body 内のコード

:(defun foo ()

 (macrolet ((bar ()
              (baz)))
   (bar)))

それらや、類似した個所の xref 情報を記録する方法のためのアイデアはいくつかありますが、まだ詳細な仕事はできていません。 もし、上の例以外の状況で、 xref が期待通りに動作しない場所を見つけたなら、是非教えてください。

新しい xref を使うには、 CVS 版の SLIME で、通常の Slime のクロスリファレンス用コマンド を使用してください。 xref のための作業(と、blog には書いていませんでしたが、いくつかの SBCL の改良のための作業)に出資してくれた ITA Software に感謝します。

3.2.7 Slime のクロスリファレンス用コマンド (Cross-reference) の翻訳

SLIME のクロスリファレンス用コマンドは、多種多様な Lisp システム毎にサ ポートされている機能に基づいています。組み込みの XREF サポートのない処 理系に対しては、SLIME はポータブルな XREF パッケージ(これは、CMU AI Repository からもってきて、SLIME に同梱されています)を使って問い合わせを行います。

それぞれのコマンドは、ポイント位置のシンボルに対して動作します。シンボルが なければ、プロンプトを表示します。プリフィックス引数を使うと、必ずプロンプトを 表示するようになります。

C-c C-w c
slime-who-calls
呼び出し元の関数を表示します
C-c C-w r
slime-who-references
グローバル変数への参照を表示します
C-c C-w b
slime-who-binds
グローバル変数の束縛を表示します
C-c C-w s
slime-who-sets
グローバル変数への値の設定を表示します
C-c C-w m
slime-who-macroexpands
マクロ展開個所を表示します
M-x slime-who-specializes
あるクラスに特定化されるすべてのメソッドを表示します

また、 呼び出し元/呼び出し先表示 コマンドもあります。これらの操作は、関数オブジェクトを通じて下位のヒープからコールグラフを探索します。 これらは、いくつかの Lisp 処理系でのみ利用可能で、詳細な XREF 情報が利用不可能な場合なもっとも役に立つ代用品です。

C-c <
slime-list-callers
呼び出し元 (caller) 関数を表示します
C-c >
slime-list-callees
呼び出し先 (callee) 関数を表示します

マニュアル翻訳

5 性能,効率

後で修正する: CMUCL の良い性能を得るための章の内容をレビューして, Texinfo で再フォーマットして SBCL 向けにちょっと改変しては挿入しましょ う.それまでの間,オリジナルの CMUCL マニュアルの内容は 95% 以上 SBCL バージョンの Python コンパイラ (訳注: CMUCL, SBCL のネイティブコードコ ンパイラの名前が Python といいます) にも適用できます.次の章を参照してください.

  • Advanced Compiler Use and Efficiency Hints
  • Advanced Compiler Introduction
  • More About Types in Python
  • Type Inference
  • Source Optimization
  • Tail Recursion
  • Local Call
  • Block Compilation
  • Inline Expansion
  • Object Representation
  • Numbers
  • General Efficiency Hints
  • Efficiency Notes

CMUCL マニュアルのこれらの情報に加えて,いくつかの事を心にとめておいてください.

  • CMUCL マニュアルでは明示的に示されていませんが,Python は代入時に型推 論を停止してしまう事があります.Python は let, let*, インラインフォー ムなどに関しては非常に積極的で賢いです.しかし,それは受動的であり setq, setf,やその同類の代入に関しては頭が悪いです.これを修正できれ ば良いのですが,代入に関するすべての型に関して満足がいくほど賢い振舞 いを期待することはできないでしょう.(代入する変数に明示的な型宣言を 行なう事によって改善する事とは無関係です.この能力を変数の型宣言なし で得る必要があります)
  • CMUCL のマニュアルが作成されてから,CMUCL(当然 SBCL も)は世代別ガベー ジコレクターを備えるようになりました.これはメモリの使用に関するいく つかの効率化のパターンが CMUCL のマニュアルで議論されているケースと異 なる事を意味します.(新しい事実について ここに 記述される必要があります)
  • SBCL はいくつかの重要な性能問題を抱えています.最も重要なものは
    • ANSI 規格で制定されている dynamic-extent 宣言のサポートに制限がついている事
    • ガベージコレクターの効率が最適化されていない.少なくとも世代別ガベー ジコレクターを備えていないプラットフォーム(SBCL 0.8.9 では x86 以外 の全てのプラットフォームが該当)では.
    • CLOS の PCL 実装は様々な面で必要以上に効率が悪い所があります.

最後に Common Lisp は多くの部分を次のフレーズで表す事ができます. ="効率的にコンパイルすることができます.十分に賢いコンパイラなら."= このフレーズはあまり有名ではありません.なぜなら全ての最適化を可能にす るほど賢いコンパイラを実際に作る事は現在のコンパイラ技術を上回る仕事だ からです.かわりに,場合に応じたコードを手書きする事による最適化や,あ るいは対応する手書きの最適化がない場合には全く最適化を行わないという事 になります.SBCL 0.6.3 で最適化されないケースをいつくか示します.

  • (reduce #'f x) は,x の型がコンパイル時に判明していても最適化されません
  • (position 0 some-bit-vector) のようなビットベクタ操作
  • (remove item list :count 1) 特定のシーケンスに対するイディオム
  • (locally (declare (safety 1)) (assoc item list)) のような局所コンパ イルポリシーが型チェックを要求しないケース.(現在では assoc するため に内部的に endp のチェックが行なわれます)

もしあなたのシステムの性能が,「効率的にコンパイルできる構成要素から成っ ているのにおそい」というケースに遭遇したら,SBCL コンパイラはそれを効率 的にコンパイルする事ができないという事です.コンパイラへのパッチを書い てそれを送ってください.直接対象となるようなコードは十分直接的に書く事 ができます; ソース注の "deftransform" という文字列を検索してみてくださ い.沢山のサンプルがみつかるはずです.

5.1 Dynamic-extent allocation

SBCL はスタック上へのメモリアロケーションについて限定的なサポートを提供 しています.dynamic-extent 宣言は検証されません.コンパイラは無条件に信 頼されます.もし,Common Lisp 標準の制約に違反すると,うまくいくとプロ グラムはごみの値を持つだけですが,大抵の場合はシステムがクラッシュします.

この結果,大抵のスタックアロケーションは厳しい条件の元に行なわれる事に なります.speed や space の最適化値はその時点の safety や debug のより も高くなければなりません.たとえば

:(locally

 (declare (optimize speed (safety 1) (debug 1)))
 (defun foo (&rest rest)
   (declare (dynamic-extent rest))
   (length rest)))

これは &rest リストはスタック上に確保されますが,次のコードではスタック 上に確保されないという事になります.

:(defun foo (&rest rest)

 (declare (optimize speed (safety 1) (debug 1)))
 (declare (dynamic-extent rest))
 (length rest))

なぜならば,こちらも &rest リストをアロケーションしますが,その変数の束 縛は最適化宣言のスコープの外側にあるからです.

多くの場合,dynamic-extent 宣言は有効です.たとえば SBCL は次のケースの 最適化を実装しています.

  • dynamic-extent 宣言された &rest リストをスタック上にアロケーションする
  • dynamic-extent 宣言された list や list* をスタック上にアロケートする.例:
    (let ((list (list 1 2 3)))
      (declare (dynamic-extent list)
      ...))         
    

    あるいは :(flet ((f (x)

            (declare (dynamic-extent x))
            ...))
     ...
     (f (list 1 2 3))
     ...)
    
  • dynamic-extent 宣言された定義済み変数への make-array フォームをスタック上にアロケートする.結果の配列は一次元でなければならず,かつ,:element-type キーワード引数のみが使用できる.

    スタック領域には上限があることに注意すべきである.巨大なベクタをスタッ ク上に確保すると,スタックオーバーフローを発生させたり,SBCL プロセスが 以上終了する原因となる.

  • dynamic-extent 宣言された flet や labels で定義されたクロージャーをスタッ ク上にアロケートする.ただし,(クロージャの内外を問わず) 代入閉包された 変数はヒープ上にアロケートされる.最適化オプション safety を 0 に設定し ないかぎり,ブロックやタグも同様にヒープに確保される.

以下のようなものが計画されています.

  • dynamic-extent 宣言されたクロージャのスタックアロケーション
  • dynamic-exten 宣言された list, list*, cons のスタックアロケーション (初期化や値のバインドも含む)
  • dynamic-extent 宣言されていない場合でも関数を適用する場合のいくつかの デフォルトや &rest リストに関する一般的なイディオムの自動検出する
  • dynamic-extent 宣言されていない場合のクロージャ呼び出しの一般的なイディオムの自動検出
5.2 Modular arithmetic

いくつかの算術関数はある特性 – N 以下のビットの演算結果が N ビット以下 になる – を持っています.もしコンパイラがこのような式 (logand, exp, mask) を発見したらそれらの式の関数ツリーやマスク結果の型は,=w= をビッ ト幅とすると (unsigned-byte w) 以下になることや,すべての中間結果が w ビットに切り詰められる,といった事がわかります.これによって,単 純な機械語コードを生成する事ができます.

次の例を見てみましょう.

:(defun i (x y)

 (declare (type (unsigned-byte 32) x y))
 (ldb (byte 32 0) (logxor x (lognot y))))

(lognot y) の結果は負で,かつ (signed-byte 33) という型になるか もしれません.これは 32 bit プラットフォーム上ではネイティブな 32-bit 演算で扱うことができません.しかし, modular arithmetic 最適化はこれを 可能にします.なぜなら結果が 32 bit に切り詰められるため,コンパイラは logxor や lognot を 32 bit の結果へと切り詰めるバージョンで置き替え,あ らに変数 x, y の型も (unsigned-byte 32) であるため 32bit 演算か利用 できるようになります.

SBCL 0.8.5 からは +, -, logand, logior, logxor, lognot とその組み合わせ, 第二引数が正の ash,などの関数が対象となっています. 良いビット幅 は HPPA, MIPS, PPC, Sparc, x86 では 32 bit, Alpha では 64 bit です.よ り小さな bit 幅のサポートも可能ではありますが,現在は実装されていません.

11 スレッド

SBCL はホストオペレーティングシステムのスレッドもしくは軽量プロセスにマッ プした低レベルのスレッドインターフェースをサポートします.これはマシン に一つ以上の CPU がある場合,スレッドがハードウェアマルチプロセシングの 恩恵を受けられるという事を意味します.しかし,Lisp からスケジューラを制 御する事はできません.これらの機能は SB-THREAD パッケージで提供されます.

この機能を使用するには x86/x86-64 かつ Linux カーネル 2.6 で NPTL が有 効になったシステムが必要です.

11.1 Threading basics
(make-thread (lambda () (write-line "Hello, world")))

Structure: sb-thread:thread Class precedence list: thread, structure-object, t

.. Thread type. Do not rely on threads being structs as it may change in future versions.

スレッド型です.スレッドが依存している構造は将来のバージョンで変更になる可能性があります.

Variable: sb-thread:*current-thread*

.. Bound in each thread to the thread itself.

全てのスレッドにおいて,実行中のスレッド自身に束縛されます.

Function: sb-thread:make-thread function &key name

.. Create a new thread of name that runs function. When the function returns the thread exits.

function で指定した関数と name で指定した名前を元に新しいスレッドを作成します. 関数がリターンするとスレッドが終了します.

Function: sb-thread:thread-alive-p thread

.. Check if thread is running.

スレッドが生存しているかどうかをチェックします.

Function: sb-thread:list-all-threads

.. Return a list of the live threads.

現在生存しているスレッドのリストを返します.

Condition: sb-thread:interrupt-thread-error

Class precedence list: interrupt-thread-error, error, serious-condition, condition, t

.. Interrupting thread failed.

スレッドでの割り込みエラーです.

Function: sb-thread:interrupt-thread-error-thread condition

.. The thread that was not interrupted.

スレッドは割り込まれません.

Function: sb-thread:interrupt-thread thread function

.. Interrupt the live thread and make it run function. A moderate degree of care is expected for use of interrupt-thread, due to its nature: if you interrupt a thread that was holding important locks then do something that turns out to need those locks, you probably won't like the effect.

生存しているスレッドに割り込んで,関数を実行します.そこで行なう変更 の加減は,割り込まれるスレッドの使用で予想される範囲に留めるべきです. もし割り込んだスレッドが重要なロックを保持しており,そのロックを必要 となるなにかを実行しようとすると望みの効果は得られないでしょう.

Function: sb-thread:terminate-thread thread

.. Terminate the thread identified by thread, by causing it to run sb-ext:quit - the usual cleanup forms will be evaluated

thread で指定されるスレッドを終了します.=sb-ext:quit= によって終了され,クリーンナップフォームが評価されます.

11.2 Special Variables

複数スレッドによるスペシャル変数の取り扱いは,他の実装と非常に似た挙動 で,大抵予想したとおりに動きます.

グローバルなスペシャル変数はすべてのスレッドで見えます.バインドされた値(例えば LET を使った場合など)はスレッドローカルになります.スレッドは 親のスレッドからダイナミックバインディングを継承しません.つまり

:(defparameter x 0) :(let ((x 1))

 (sb-thread:make-thread (lambda () (print *x*))))

は 1 ではなく 0 を表示します.

11.3 Mutex Support

Mutex は共有リソースのアクセスコントールに使われます.一つのスレッドが ミューテックスを保持すると,他のスレッドはそれが解放されるまで待つ事に なります.起こされるまで,スレッドは sleep します.

ミューテックスの要求にタイムアウトはありません.しかし,ウエイトを使い たい場合には WITH-TIMEOUT マクロ(これは,N 秒後に TIMEOUT コン ディションを通知します)を使う事ができます.

:(defpackage :demo (:use "CL" "SB-THREAD" "SB-EXT"))


:(in-package :demo)


:(defvar a-mutex (make-mutex :name "my lock"))


:(defun thread-fn ()

 (format t "Thread ~A running ~%" *current-thread*)
 (with-mutex (*a-mutex*)
   (format t "Thread ~A got the lock~%" *current-thread*)
   (sleep (random 5)))
 (format t "Thread ~A dropped lock, dying now~%" *current-thread*)))

:(make-thread #'thread-fn) :(make-thread #'thread-fn)

Structure: sb-thread:mutex

Class precedence list: mutex, structure-object, t

Mutex 型です.

Function: sb-thread:make-mutex &key name value

Mutex を作成します.

Function: sb-thread:mutex-name instance

mutex の名前へのアクセサです. setf 可能です.

Function: sb-thread:mutex-value instance

mutex の値です.もし解放されている Free 状態ならば nil になります. setf 可能です.

Function: sb-thread:get-mutex mutex &optional new-value wait-p

mutex を取得し,値が NIL ならば新しい値,もしくはデフォルト値を設 定します.もし wait-p に非 NIL が指定されており,かつ Mutex が使用中であるあんらば,利用可能になるまで sleep します.

Function: sb-thread:release-mutex mutex

mutex の値を nil に設定し,解放します.この Mutex を待っていたスレッ ドを起こします.

Macro: sb-thread:with-mutex (mutex &key value wait-p) &body body

body のダイナミックスコープで mutex を取得します. もし値が nil ならば value で指定された値もしくはデフォルト値が設定されます. wait-p に非 NIL が指定されており,かつ mutex が使用中であ るなら,利用可能になるまで sleep します.

Macro: sb-thread:with-recursive-lock (mutex) &body body

body のダイナミックスコープで Mutex を取得します.同じ mutex に対してスコープ内での再帰的なロックが成功します.値としてデフォルト 値を用いている同じ mutex に対する with-mutexwith-recursive-lock を混ぜて使用する事が許可されています.

11.4 Waitqueue/condition variables

POSIX 状態変数の設計をベースにしていますが,それゆえ CL 的に無作法な名 前の衝突があります.状態変数をチェックし,それが新になるまでスリープす るといった使い型があるでしょう.例えば,共有されたキューに対して,書き 込みプロセスは「キューが空」である事をチェックし,一つもしくは複数の読 み出しプロセスが「キューが空でなくなった」時を知る必要があるとします. これはシンプルに聞こえますが,他のプロセスがあなたの予想していない時に 動くと驚くほど簡単にデッドロックします.

これは三つのコンポーネントで構成されます.

  • 状態
  • 状態を代行する( waitqueue のような)状態変数
  • 状態を評価する間に保持するロック

特に気をつけるべき点は,

  • condition-wait を呼ぶ時に,Mutex を保持する必要があります.=condition-wait= はそれを待つ間 mutex を解放します.そして,何らかの理由で帰って くる前に再びそれを取得します.
  • 同様に condition-notify を呼ぶ場合にも mutex を保持している必要があります
  • condition-wait からプロセスが帰ってくるのにはいくつかの状況が考えられます. conditiontrue になっているという事は保証されません.したがって目的に応じてリソースが利用可能になっているかどうかをチェックする必要があります.

:(defvar buffer-queue (make-waitqueue)) :(defvar buffer-lock (make-mutex :name "buffer lock"))


:(defvar buffer (list nil))


:(defun reader ()

 (with-mutex (*buffer-lock*)
   (loop
    (condition-wait *buffer-queue* *buffer-lock*)
    (loop
     (unless *buffer* (return))
     (let ((head (car *buffer*)))
       (setf *buffer* (cdr *buffer*))
       (format t "reader ~A woke, read ~A~%"
               *current-thread* head))))))

:(defun writer ()

 (loop
  (sleep (random 5))
  (with-mutex (*buffer-lock*)
    (let ((el (intern
               (string (code-char
                        (+ (char-code #\A) (random 26)))))))
      (setf *buffer* (cons el *buffer*)))
    (condition-notify *buffer-queue*))))

:(make-thread #'writer) :(make-thread #'reader) :(make-thread #'reader)

Structure: sb-thread:waitqueue

Class precedence list: waitqueue, structure-object, t

Waitqueue 型です.

Function: sb-thread:make-waitqueue &key name

Waitqueue を作成します.

Function: sb-thread:waitqueue-name instance

Waitqueue の名前へのアクセサです. setf 可能です.

Function: sb-thread:condition-wait queue mutex

自動的に mutex が解放され,そして自分自身がキューに入れられます. 他のスレッドが condition-notify を用いて通知してくると,再び mutex が取得され,そして呼び出し元にリターンします.

Function: sb-thread:condition-notify queue &optional n

キューで待っているスレッドに n を通知します.

Function: sb-thread:condition-broadcast queue

キューで待っている全てのスレッドに通知します.

11.5 Sessions/Debugging

もし,ユーザーが同じ Lisp イメージ上に複数のビュー(例えば,複数の端末 やウィンドウシステム,ネットワーク経由のアクセスなど)を持っているなら ば,典型的にはそれぞれのビューに対してそれぞれのセッション (フォアグラ ンド/バッググラウンド/ ストップ スレッドのコレクション) が割り当てられ ます.新しいセッション作成したいスレッドは, sb-thread:with-new-session を使うことで自分自身をカレントセッション から決して新しいセッションを作成できます. sb-thread:make-listener-thread も参照してください.

一つのセッションの中でのスレッド間の調整はユーザーが注意を払う必要があ ります.スレッドは foreground , background , stopped の 3 つの状態のうちのいずれかの状態を取ります.もし background のプオセ スが REPL のプロンプトを表示しようとしたり,デバッガに入ろうとすると, そのプロセスは停止され,そして停止された旨のメッセージが表示されます. ユーザーは必要に応じてそのようなスレッドにスイッチする必要があります. もし background スレッドがデバッガに入るとそれが中断される前にバッ クグランドに押し戻せるような選択がなされます.入力ストリームに対する調 整は, sb-thread::get-foreground (これはブロックします) や sb-thread::release-foreground を呼ぶ事によって管理されます.

sb-ext:quit はカレントセッションの全てのスレッドを終了しますが,他 の稼働中のセッションを終了させる事はありません.

11.6 Implementation (Linux x86)

スレッドは pthread といくつかの Linux 特有の機能(futex など)を使って 実装されています.

x86 上では,スペシャル変数へのスレッドローカルな束縛は %fs セグメントレ ジスタがスレッド毎の記憶領域を指すという機能を用いて実現されています. これは,もしスレッド動作する事を前提とする,もしくは新にスレッドを作成 する外部コードとリンクし,そのスレッドライブラリが %fs を非互換な方法で 使用していると面白い結果を引き起こす事があります.x86-64 では r12 レジ スタが同様の役割を果します.

キューは sys_futex() システムコールが利用可能である事を要求します. これが, NPTL が要求される理由です.実行時にこのシステムコールが利用可 能である事がテストされます.

ガベージコレクションは既存のコンサバティブ世代別 GC によって実施されま す.アロケーションは小さな領域(典型的には 8K)単位で実施され,すべての スレッドは自身のリージョンをもっています.しかし,リージョンが埋め尽さ れた時,アロケートしている間はロックが必要となります.ガベージコレクショ ンが必要になった時には全てのプロセスが停止します.これはシグナルを送る 事によって実現されています.これはシステムコールに対して割り込みをかけ て奇妙な振舞いを起こすかもしれません.ストリームインターフェースはシス テムコールを正しく再開できる事が要求されていると思いますが,他のブロッ キングな呼び出し(外部ライブラリコード)などについては考慮が必要です.

おおくの SBCL ライブラリはスレッドセーフである事が検証されていません.

コンパイルや fasl のロードなど並列化できない,いくつかの明らかにアンセー フな領域のものは大きくロックされています.これらの領域での作業は現在進 行中です.

新たに作成されたスレッドはデフォルトで同じ POSIX プロセスグループと作成 元スレッドと同じセッションに属します.これはキーボード割こ込みの取り扱 いに影響します.端末の intr キー (Control-C) を押すと,SBCL では通常 background となっている Lisp スレッドを含んだ同じフォアグラウンドプ ロセスグループの全てのプロセスに割り込みがかかるからです.これが望まし くない場合には,バックグラウンドスレッドは SIGINT シグナルを無視す るように設定るう必要があります.

sb-thread:make-listener-thread は新しい POSIX セッションとなる Lisp セッションを作成するために追加されました.これを使えば,あるウィン ドウで Control-C を押しても,他の Listener に割り込みがかかる事はありま せん.