/ 最近 .rdf 追記 編集 設定 本棚

脳log[20201207]



2020年12月07日 (月) [Ruby] 2.7. 配列の複製。a.dup と *a の比較で dup の方が速いと書くツイートを読んだが、dup の速さは copy on write 的な速さじゃないかと疑問に思った。テストスクリプト>a.rb。速い順> dup > *A& > dup& > *A。タイム> 0.0003 > 8.0 > 8.2 > 8.4。書き込みまで含めれば dup も速くない。順番を入れ替えたりしても一貫して *A (ただの複製) より *A& (複製&代入) の方が速いのが謎であり何か失敗しているのかもしれないが……。■別バージョン>b.rb。配列のサイズと繰り返し回数の大小を入れ替えたもの。速い順> dup > *A > *A& > dup&。タイム> 0.3 > 2.5 > 2.5 > 3.2。おそらくコピーをツケにしている dup はさておき、dup& の劣勢が明らか。コピーすべき配列のサイズが小さい反面、Array#dup の呼び出し回数がかさんだことが理由では? *A はメソッド呼び出しではなくて文法的なアレ(←語彙力)だったりする(かもしれない)のが有利なのでは? 文法といえば while 文ですが……(while は速い)。■Array#dup が速いのは嬉しい。たとえば配列を更新しながら何度も yield するとき>20190907p01.03。yield された方で破壊的変更を加えたときにメソッドの継続に支障を来してはいけないが、メソッドの使用方法に注意書きを添えるのでは不十分。かといって都度都度配列のコピーを yield するのも、ほとんどの場合には無駄となる。本当に必要になるまでコピーが遅延されるなら嬉しい。「~なら」って書くのは、実際のところ https://github.com/ruby/ruby/blob/master/array.c#L2654 からコールグラフを辿っていってもよくわからないから、っていうか本当にコピーしているようにしか見えないからなんだけど。■array.c の末尾を見ると Array#dup の実体が rb_ary_dup だという事実はなさそう。object.c を見ると dup の実体は rb_obj_dup らしい。そこのコメントを読むとこの(dup)メソッドはクラスごとに固有のふるまいを見せるだろうから、クラスの initialize_copy メソッドのドキュメントを読むんだよ、と書いてある。再び array.c に戻って Array#initialize_copy の実体が rb_ary_replace であることを確認するんだけど、それではなくて rb_ary_shared_with_p のコメントを読むとこうある。「このメソッドは、たとえば rb_ary_replace において、配列のスナップショットがとられて、それから変更が加えられたかどうかのチェックに使われる。スナップショットは安いがスナップショットに変更を加えたときにはコピーのコストが発生する。pop, shift を呼ぶかぎりにおいては配列の共有は維持される。」みたいな。■Array#dup の実体がどれだかちょっと曖昧だけど、配列の全体なり部分なりを共有してコピーを先送りする仕組みがあるのは間違いない。