K.Sasada's Home Page

Diary - 2015 December

研究日記

師走

_31(Thu)

今年行った場所。

  • 1月 ベルギー・ブリュッセル(FOSDEMで発表)
  • 2月 オランダ・アムステルダム(Amsterdam.rb に参加、パネル。FOSDEM のついで)
  • 3月 フィリピン・ボラカイ(Rubyconf.phで発表)
  • 4月 アメリカ・アトランタ(RailsConfで発表)
  • 6月 日本・栃木(栃木Ruby会議で朗読)
  • 7月 日本・大阪(関西Ruby会議で発表)
  • 9月
    • ポルトガル・ポルト(RubyConf.ptで発表)
    • ロシア・モスクワ(RailsClubで発表)
  • 10月 オーストリア・ザルツブルグ(EURUKO2015で発表)
  • 11月
    • 日本・東京(IPSJ PROで発表)
    • 日本・東京(OedoRubyKaigi05で運営)
  • 12月 日本・東京(RubyKaigi2015で発表)

日本以外は7カ国。そのほか、福岡に何度か。 そういえば、一度も松江に行っていない。 日本のいろんな都市に行ってみたいなぁ。

来年は、もうこんなには行けなさそう。 落ち着いて開発しよう。

Ruby 2.3.0 リリースできて何よりでした。

来年もよろしくお願いします。

_13(Sun)

RubyKaigi 2015 です、でした。


発表した内容について。

Koichi Sasada: Compiling Ruby scripts, RubyKaigi 2015, Tokyo, Japan (2015.12). 2015_RubyKaigi2015.pdf (1136KB), http://rubykaigi.org/2015/presentations/ko1

Ruby プログラムを、一度バイナリに落として、という感じです。 速度を強調したスライドになっていますが、ちょっと数字が出たのが速度であった、というのが本当のところで、実際はメモリ削減が主目的で始めました。というのも、ISeq 自体が結構メモリ食っていて、その上で fork すると大変な感じで増えるので、マルチプロセスではあまり食わないように、とか、そういうことを狙っていました。が、メモリ削減については、全くの手つかずです。というわけで、いい加減なところで終わっています。

とりあえず、こういうのがあったときに、どう使われるのかな、という動機で、2.3 で実験的に導入してもらいました。

メモリ削減については、

■(1) プロセス単体では、出来る箇所を read only でバイナリを参照するだけにする

■(2) n 個のマルチプロセスでは、バイナリを mmap で共有することで、消費メモリを 1/n にすることを目指す

ということが出来れば良いと思っていました。(1) はまったくやっておらず(全部読み込み、現状と同じようにコピーが走ります)、(2) は (1) をやっていないので意味が無い。実は、lazy によって、(2) の目が若干あったので、効くかなとやってみたのですが、殆ど効果がなかった。なぜ効果が無かったのか、まだよく見ていないのですが。

(1) については、抜本的にデータ構造を変更しないといけなくて、実は iseq まわりのデータ構造の変更は、それが理由です。理由ですが、十分に対応できなかった。

というわけで、誰かやりませかね。面白いと思うのですが。


コンパイル済みバイナリを、ファイルへストアするツールであり、require でコンパイル済みバイナリをロードするライブラリを、sample/iseq_load.rb として同梱しているのですが、いかんせん sample なので、使いづらい。

なので、gem として用意しました。yomikomu gemです。

インストールすると、kakidasu というコマンドラインツールと、yomikomu.rb というライブラリが導入されます。

$ kakidasu

とすることで、lib 以下のすべてのファイル(添付ライブラリはもとより、Gemでインストールされたものもすべて)をコンパイルします。

$ kakidasu [file or dir]

とすることで、指定もできます。

使う時は、require 'yomikomu' とした以降の require や load で読み込むライブラリで、可能ならコンパイル済みバイナリから読み込むようになります。

良かったら試してみて下さい。


このどさくさに紛れて、実はもうちょっと面白い機能も突っ込んでいるのですが、それは別に紹介します。


書いたので紹介します。ISeq.translate(iseq) というメソッドが定義されていると、iseq を途中で差し替えることができるようになりました。

require 'fiddle'
require 'pp'

class RubyVM::InstructionSequence
  address = Fiddle::Handle::DEFAULT['rb_iseq_load']
  func = Fiddle::Function.new(address, [Fiddle::TYPE_VOIDP] * 3, Fiddle::TYPE_VOIDP)
  define_singleton_method(:load_from_array) do |data, parent = nil, opt = nil|
    func.call(Fiddle.dlwrap(data), parent, opt).to_value
  end

  def self.translate iseq
    ary = iseq.to_a
    ary[13].each{|insn|
      if Array === insn && insn[0] == :putstring
        insn[1] = "#{insn[1]}!!"
      end
    }
    load_from_array(ary)
  end
end

eval("puts 'Hello World'")

結果は...。

テスト用にとりあえず入れたのですが、実は JIT とかで差し替えたものを入れる、みたいな用途が考えられます。ほんとかな。


TRICK 2015 で Most inconsistent, Most timely awards を受賞しました。ありがとうございます。どちらも一発ネタで、すぐに書けたものでした。他の人達と比べて、全く難しくなくて、とにかく恐縮です。


4件送って、時間をとてもかけたのは、やはり技巧がまったく足りず、遠藤さんの本の単なるフォロアーだったという。

__________________ _
____________________ _
______________________ _
_______________________ _
_______________________ _
_______________________ _
______________________ _
____________________ _
__________________ _
_____________ _
________ _
_______ _
________ _
____________ _
__________________ _
__________________________ _
________________________________ _
__________________________________ _
________________________________ _
__________________________ _
__________________ _
________________ _
__________________ _
________________________ _
____________________________ _
_____________________________ _
____________________________ _
________________________ _
__________________ _
________________ _
______________ _
_____________ _
_____________ _
_____________ _
______________ _
________________ _
__________________ _
____________________ _
__________________ _
________________ _
_______________ _
______________ _
______________ _
______________ _
_______________ _
________________ _
__________________ _
______________________ _
________________________ _
_________________________ _
________________________ _
______________________ _
__________________ _
_________________ _
________________ _
_______________ _
_______________ _
_______________ _
________________ _
_________________ _
__________________ _
_____________________ _
_______________________ _
________________________ _
_________________________ _
________________________ _
_______________________ _
_____________________ _
__________________ _
_____________ _
________ _
_______ _
________ _
____________ _
__________________ _
____________________ _
_____________________ _
______________________ _
______________________ _
______________________ _
_____________________ _
____________________ _
__________________ _
_______________ _
____________ _
___________ _
____________ _
______________ _
__________________ _
_____________________ _
_______________________ _
________________________ _
_______________________ _
_____________________ _
__________________ __
BEGIN{W=18;@w=[];def
method_missing(m,
w=$,)(w)?(if(c=
(m.size)-W)==$.
@w.empty?&&
return
x,y=(
@w.max-
2),@w.size+
1;@w=[];c=((y/
2)*16+8+8+x).chr
print(c.downcase)
else;@w<<c.abs
end):m;end}

単に実行するだけ。 これも時事ネタでした。


 ++++        ---    +++++++      ---     ++++++      ---    +++  +++
+++++        ---    ++++++++     ---    ++++++++     ---    +++  +++
   ++        ---         +++     ---         +++     ---    +++  +++
   ++     --------  ++++++++  --------  +++++++   --------  ++++++++
   ++     --------  ++++++++  --------  +++++++   --------  ++++++++
   ++        ---    +++          ---         +++     ---         +++
++++++++     ---    ++++++++     ---    ++++++++     ---         +++
++++++++     ---     +++++++     ---     ++++++      ---         +++

START!;BEGIN{eval(($p=%q(M,@p,S=4,0,"\x1f".succ;define_method(:method_
missing){|m,*_|(m==:+@)?(@p+=1):(m==:-@)?(@p*=12345):nil;self};END{nn=
->(n,c='+'){(0..7).map{|y|(0..7).inject(S*8){|r,x|(n[y*8+x]!=0)&&(r[x]
=c);r}}.join("\n")};mm=->(m){m.each_line.map{|line|line.chomp.each_cha
r.map{|c|['+','-'].include?(c)?c:nil}}};jj=->(ms){pl=Array.new(8){Arra
y.new(ms.length*10){S}};ms.each.with_index{|m,i|(mm.(m)).each.with_ind
ex{|line,y|li                                         ne.each.with_ind
ex{|e,x|e&&pl                                         [y][x+10*i]=e}}}
;pl.map{|e|e.                                         join.rstrip}.joi
n("\n")};pp=-                                         >(ms){puts(jj.(m
s))};ee=->(ms){@p=0;eval(jj.(ms)+'_');@p};nm=->(ns){ns.map{|n|(Fixnum=
==n)?MS[n]:P}};ss=->(tp){M.times{|i|(0..9).to_a.repeated_permutation(1
+i){|ns|i.times.each{|j|ns.insert(j*2+1,:+)};(tp==ee.(nm.(ns)))&&(pp.(
eval(ns.map(&:to_s).join).to_s.split(//).map{|s|MS[s.to_i]});exit)}}};
MS=[439548674                                         4551063324,18446
4890904953567                                         02,1837441380078
5903487,91512                                         79806051057534,1
6204198849393                                         715175,915128035
1497682815,9151288054521495358,2025524961147879294,9151288054536175422
,9151280353659813694].map{|n|nn.(n)};P=nn.(4051050536969451576,'-');AR
GV.empty??ss.(@p):((ARGV.size>M)&&raise('too_many');ms=ARGV.map{|n|MS[
n.to_i]||raise("too_big")};(ms.size-1).times{|i|ms.insert(i*2+1,P)};$B
='BEGIN';$E='START!';$e='eval';$s=('[').succ;pp.(ms);puts;puts(%Q{#$E;
#$B{#$e(($p=%q(#$p);$p.gsub(/#{$s}s*/,'')))}}))});$p.gsub(/\s*/,'')))}

コードゴルフ力がなくて長すぎ。 電卓です。単に実行すると、1 + 2 + 3 + 4 の結果を返します。

$ ruby entry.rb 3 4 5

みたいな感じで、数字をくっつけると、その数字を計算するプログラムを出します。


次があれば頑張りたいですが、まったく他の作品に追いつける気がしない。

http://www.rubyinside.com/advent2006/5-negaposi.html から、+ と - に頑張って貰う何かがテーマなのですが、次があれば、また何か。String#+@ とか入れたから、なんとかならないかな。ならなそうだな。

Sasada Koichi / ko1 at atdot dot net
$Date: 2003/04/28 10:27:51 $