2014-04-23-01-本気で move について考える - 3
>> Site top >> weblog >> 月別アーカイブ >> 2014年04月のlog >> 2014-04-23-01-本気で move について考える - 3
最終更新日付:2014/04/23 23:50:00
本気で move について考える - 3
2014 年 04 月 23 日
さて、できるだけのことはやったつもり。では、できたこととできなかったことを。
数日前には、自分は以下のようなことを要件としていた。
- move の結果を代入元に指定することで、ムーブが実現できる。
- move の結果を他の処理に『渡す』ことができ、その先でムーブが実行できなければならない。
- 上記の方法で渡しても、普通の参照のようにも使えなければならない。
- 実際にムーブを実行する場合、もう一度 move を使う。
ひとまずのところ、1 と 2 はできたと思って良いだろう。ただし、setf では使えない。opr= でなければならないのだ。これが重要なポイントの1つだ。そして、3 は完全に無視してしまった。どう考えてもできるとは思えなかったからだ。4 については、やらなくてもできてしまい、やっても無害である。それもどうかと思うが、これも仕方ないかな、と思っている。
では、パフォーマンスはどうだろうか。そもそも、opr= による代入が一般的に( setf と比べれば)コスト高になりそうだったからムーブをなんとかして実現しようとしたはずだった。しかし今日までの間に、どういうわけだかムーブは opr= 経由でないと動作せず、しかも追加の __move-2 という総称関数コールを必要とすることになってしまったのだ。今の自分の力量ではこれが限界というところだが、もう少しエレガントに解決できたらいいのにとは思う。
それはさておき、ひとまず展開形を見てみよう。普通に move と opr= を利用してムーブを行う式を展開する。今使っているのは sbcl なので、sb-cltl2:macroexpand-all を使用している。なお、空の let は手作業で消し、しかも小文字に変えてある。だって大文字だとなんか読み難いんだもん。
* (sb-cltl2:macroexpand-all '(opr= vec1 (move vec2))) => (let ((#:new779 (operator= vec1 (__move-1 (lambda (&optional (op '#:get750)) (if (eq op '#:get750) vec2 (let ((#:new780 op)) (setq vec2 #:new780) nil))))))) (setq vec1 #:new779) vec1)
opr= マクロにも move マクロにも、まだ少し削れるところがあるがそれはさておいて、ポイントは以下の通り。
- レキシカルクロージャを作る。
- __move-1 関数をコールする。その中で remove-reference を make-instance している。
- 総称関数 operator= をコールする。そのデフォルトの実装では、さらに総称関数 __move-2 がコールされる。
- これら全てから復帰して、初めて代入先への値の設定ができる。
‥‥‥なんか、ものスゴく大変なことをしているような気がする。しかし、C++ で T t2 = std::move( t1 ); と書くのを模倣するとなると、これくらいしか思いつかないのだ。あ、それで思い出した。以下のことも考えないといけない。
- C++ でのオブジェクト間ムーブの模倣だけでなく、nil <- obj タイプのムーブの一般的なルール策定。
- ムーブコンストラクタのこと(上記にも関連する)。
- range move アルゴリズムをちゃんと move で実現する(以前のはインチキ過ぎた)。
続きはまた。
コメント
このページのタグ
Page tag : Common Lisp
Page tag : STLとその移植
Copyright(C) 2005-2018 project-enigma.
Generated by CL-PREFAB.