トップ 最新 追記

3 日坊主日記


2007-03-01 [長年日記]

_ [Ruby][Rails] るびま 0018

今回はRoR編集を担当しました。 かんさんにはかなり早く原稿いただいたのに発行が遅れて申し訳ない。

DB2は流石IBMだけあってインストーラとかドキュメントがすばらしい。 常駐メモリを食うのが難。


2007-03-03 [長年日記]

_ rake for ruby 1.9

rake が難しい。

うー。yield まわりぽいけど。

_ 再現しない

[BUG] を mingw32 で切り詰めよしよしと思って linux に持っていったら再現しない。なぜだ!

module Rake

  class HaunTask
    def initialize
      Rake.app.define_task { p :here }
    end
  end

  def self.app
    @app ||= Rake::App.new
  end

  class App
    def initialize
      @t = Hash.new
    end

    def define_task(&b)
      @t["hu"] ||= Rake::Task.new
      t = @t["hu"]
      t.enhance(&b)
      t
    end

    def run
      Rake::HaunTask.new
      @t["hu"].execute
    end
  end

  class Task
    def initialize
      @act = nil
    end

    def enhance(&b)
      @act = b
    end

    def execute
      GC.start
      @act.call(self)
    end

  end
end

Rake.app.run

_ collaboa

設置してみました。


2007-03-09 [長年日記]

_ rubygems for ruby-1.9

$ ruby-1.9.0 -v
ruby 1.9.0 (2007-03-05 patchlevel 0) [i386-mingw32]

RubyGems unpack が ruby-1.9 で動かないので調査中。

ERROR:  While executing gem ... (Zlib::GzipFile::Error)
    not in gzip format

Zlib::GzipReader がうまく動いていない。

# gzipreader_io_test.rb:
fname = "metadata.gz"
io = File.open(fname)

class Reader
  def initialize(io)
    @io = io
  end
  def read(*args)
    p [:read, *args]
    @io.read(*args)
  end
end
io = Reader.new(io)

require "zlib"
p Zlib::GzipReader.new(io)
$ ruby-1.8.6 gzipreader_io_test.rb
[:read, 2048]
#<Zlib::GzipReader:0x293ef20>
$ ruby-1.9.0 gzipreader_io_test.rb
gzipreader_io_test.rb:16:in `new': not in gzip format (Zlib::GzipFile::Error)
        from gzipreader_io_test.rb:16:in `<main>'

Zlib::GzipReader から io.read が呼ばれていない。

# zlib.c:
static VALUE
gzfile_read_raw_partial(VALUE arg)
{
    struct gzfile *gz = (struct gzfile*)arg;
    VALUE str;

    str = rb_funcall(gz->io, id_readpartial, 1, INT2FIX(GZFILE_READ_SIZE));
    Check_Type(str, T_STRING);
    return str;
}

static VALUE
gzfile_read_raw_rescue(VALUE arg)
{
    struct gzfile *gz = (struct gzfile*)arg;
    VALUE str = Qnil;
    if (rb_obj_is_kind_of(rb_errinfo(), rb_eNoMethodError)) {
        str = rb_funcall(gz->io, id_read, 1, INT2FIX(GZFILE_READ_SIZE));
        if (!NIL_P(str)) {
            Check_Type(str, T_STRING);
        }
    }
    return str; /* return nil when EOFError */
}

static VALUE
gzfile_read_raw(struct gzfile *gz)
{
    return rb_rescue2(gzfile_read_raw_partial, (VALUE)gz,
                      gzfile_read_raw_rescue, (VALUE)gz,
                      rb_eEOFError, rb_eNoMethodError, (VALUE)0);
}

rb_rescue2 の r_proc は第2引数に errinfo が渡る。これを試してみる。

# zlib.diff
--- ext/zlib/zlib.c	Fri Mar  9 17:14:18 2007
+++ ext/zlib/zlib.c.new	Fri Mar  9 17:00:45 2007
@@ -112,7 +112,7 @@
 static void gzfile_close _((struct gzfile*, int));
 static void gzfile_write_raw _((struct gzfile*));
 static VALUE gzfile_read_raw_partial _((VALUE));
-static VALUE gzfile_read_raw_rescue _((VALUE));
+static VALUE gzfile_read_raw_rescue _((VALUE, VALUE));
 static VALUE gzfile_read_raw _((struct gzfile*));
 static int gzfile_read_raw_ensure _((struct gzfile*, int));
 static char *gzfile_read_raw_until_zero _((struct gzfile*, long));
@@ -1755,11 +1755,11 @@
 }

 static VALUE
-gzfile_read_raw_rescue(VALUE arg)
+gzfile_read_raw_rescue(VALUE arg, VALUE errinfo)
 {
     struct gzfile *gz = (struct gzfile*)arg;
     VALUE str = Qnil;
-    if (rb_obj_is_kind_of(rb_errinfo(), rb_eNoMethodError)) {
+    if (rb_obj_is_kind_of(errinfo, rb_eNoMethodError)) {
         str = rb_funcall(gz->io, id_read, 1, INT2FIX(GZFILE_READ_SIZE));
         if (!NIL_P(str)) {
             Check_Type(str, T_STRING);
$ ruby-1.9.0 gzipreader_io_test.rb
[:read, 2048]
-- stack frame ------------
0000 (00C70020): 00000004
0001 (00C70024): 00000005
0002 (00C70028): 00c28398
0003 (00C7002C): 00c281e8
0004 (00C70030): 00000004
0005 (00C70034): 00000001
0006 (00C70038): 00000004
0007 (00C7003C): 00c25c08
0008 (00C70040): 00c281e8
0009 (00C70044): 00000004
0010 (00C70048): 00000001 <- lfp <- dfp
-- control frame ----------
c:0004 p:---- s:0011 b:0011 l:000010 d:000010 CFUNC  :new
c:0003 p:0073 s:0007 b:0006 l:000005 d:000005 TOP    gzipreader_io_test.rb:16
c:0002 p:---- s:0002 b:0002 l:000001 d:000001 FINISH
c:0001 p:---- s:0000 b:-001 l:000000 d:000000 ------
---------------------------
-- stack frame ------------
0000 (00C70020): 00000004
0001 (00C70024): 00000005
0002 (00C70028): 00c28398
0003 (00C7002C): 00c281e8
0004 (00C70030): 00000004
0005 (00C70034): 00000001
0006 (00C70038): 00000004
0007 (00C7003C): 00c25c08
0008 (00C70040): 00c281e8
0009 (00C70044): 00000004
0010 (00C70048): 00000001
0011 (00C7004C): 00000004
0012 (00C70050): 00000001
0013 (00C70054): 00000004
0014 (00C70058): 00000001
0015 (00C7005C): 00000004
0016 (00C70060): 00c23b38
0017 (00C70064): 00c23c10
0018 (00C70068): 00000004
0019 (00C7006C): 00000001
0020 (00C70070): 00c23b38
0021 (00C70074): 00001001
0022 (00C70078): 00000004
0023 (00C7007C): 00000001
0024 (00C70080): 00000004
0025 (00C70084): 00000001
0026 (00C70088): 00000004
0027 (00C7008C): 00000001 <- lfp <- dfp
-- control frame ----------
c:0011 p:---- s:0028 b:0028 l:000027 d:000027 CFUNC  :inspect
c:0010 p:---- s:0026 b:0026 l:000025 d:000025 CFUNC  :write
c:0009 p:---- s:0024 b:0024 l:000023 d:000023 CFUNC  :read
c:0008 p:0024 s:0020 b:0020 l:000019 d:000019 METHOD gzipreader_io_test.rb:10
c:0007 p:---- s:0016 b:0017 l:000016 d:000016 FINISH
c:0006 p:---- s:0015 b:0015 l:000014 d:000014 CFUNC  :method_missing
c:0005 p:---- s:0013 b:0013 l:000012 d:000012 CFUNC  :initialize
c:0004 p:---- s:0011 b:0011 l:000010 d:000010 CFUNC  :new
c:0003 p:0073 s:0007 b:0006 l:000005 d:000005 TOP    gzipreader_io_test.rb:16
c:0002 p:---- s:0002 b:0002 l:000001 d:000001 FINISH
c:0001 p:---- s:0000 b:-001 l:000000 d:000000 ------
---------------------------
-- stack frame ------------
0000 (00C70020): 00000004
0001 (00C70024): 00000005
0002 (00C70028): 00c28398
0003 (00C7002C): 00c281e8
0004 (00C70030): 00000004
0005 (00C70034): 00000001
0006 (00C70038): 00000004
0007 (00C7003C): 00c25c08
0008 (00C70040): 00c281e8
0009 (00C70044): 00000004
0010 (00C70048): 00000001
0011 (00C7004C): 00000004
0012 (00C70050): 00000001
0013 (00C70054): 00000004
0014 (00C70058): 00000001 <- lfp <- dfp
-- control frame ----------
c:0006 p:---- s:0015 b:0015 l:000014 d:000014 CFUNC  :method_missing
c:0005 p:---- s:0013 b:0013 l:000012 d:000012 CFUNC  :initialize
c:0004 p:---- s:0011 b:0011 l:000010 d:000010 CFUNC  :new
c:0003 p:0073 s:0007 b:0006 l:000005 d:000005 TOP    gzipreader_io_test.rb:16
c:0002 p:---- s:0002 b:0002 l:000001 d:000001 FINISH
c:0001 p:---- s:0000 b:-001 l:000000 d:000000 ------
---------------------------
DBG> : "gzipreader_io_test.rb:16:in `new'"
DBG> : "gzipreader_io_test.rb:16:in `<main>'"

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
[BUG] cfp consistency error - call0
ruby 1.9.0 (2007-03-05) [i386-mingw32]

io.read は呼ばれたけど落ちた。

本日のツッコミ(全2件) [ツッコミを入れる]

# MoonWolf [RSSのURLが間違っているようです。 moriq.rdfではなくindex.rdfでは?]

# moriq [おおっ間違ってますね。直します。 ご指摘ありがとうございます。]


2007-03-10 [長年日記]

_ [Ruby] 可視性 (ruby-1.9)

send は public なメソッドは呼べるけど private, protected なメソッドは呼べない。

funcall は public, private なメソッドは呼べるけど protected なメソッドは呼べない。

protected が分からない。

class C
  def him
  end
  public :him

  def you
  end
  protected :you

  def me
  end
  private :me
end

require 'test/unit'

class TC < Test::Unit::TestCase
  def setup
    @i = C.new
  end

  #
  # send
  #
  def test_send_him
    y { @i.send(:him) }
  end

  def test_send_you
    n { @i.send(:you) }
  end

  def test_send_me
    n { @i.send(:me) }
  end

  #
  # funcall
  #
  def test_funcall_him
    y { @i.funcall(:him) }
  end

  def test_funcall_you
    n { @i.funcall(:you) }
  end

  def test_funcall_me
    y { @i.funcall(:me) }
  end

  private
    def y(&b)
      assert_nothing_raised(&b)
    end

    def n(&b)
      assert_raise(NoMethodError, &b)
    end
end

Rails は send も protected も使いまくってる。うー。

_ [Rails] benchmark (rails for ruby 1.9)

最低限動くようになったので benchmark をとってみた。

# Linux can.moriq.com 2.6.12-2.3.legacy_FC3smp

まず AR のみ。AR の benchmarks を元に書いた。

# sqlite3-ruby select (n=1000)
ruby-1.8.6: 2142 req/sec
ruby-1.8.6: 2153 req/sec
ruby-1.9.0: 2313 req/sec
ruby-1.9.0: 2328 req/sec
# activerecord -> sqlite3-ruby select (n=1000)
ruby-1.8.6: 1098 req/sec
ruby-1.8.6: 1103 req/sec
ruby-1.9.0:  801 req/sec
ruby-1.9.0:  806 req/sec

次に dispatch 以降全体。railsbench を元に書いた。

gem をなるべく使わない環境を用意。

  • vendor/rails (rails:freeze:gems)
  • rake on site_ruby
  • rubygems on site_ruby
  • sqlite3-ruby on site_ruby

Music model: schema としては genre:int column だけ持つ。 実際の record はひとつもない状態で計測。

musics は scaffold Music で作った controller。 musics/index は paginate limit 20 だけど record がないのであまり影響しない。

----------------------------------------
Time: Sat, Mar 10 2007 15:27:28 +0900
Ruby version: 1.8.6
                                      user     system      total        real
load environment                  0.730000   0.090000   0.820000 (  0.820679)
/musics/index                     0.810000   0.120000   0.930000 (  0.928919)
----------------------------------------
Time: 2007-03-10 14:32:31 +0900
Ruby version: 1.9.0
                                      user     system      total        real
load environment                  1.120000   0.090000   1.210000 (  1.222148)
/musics/index                     0.670000   0.120000   0.790000 (  0.795256)

n=100 なので n/real では

1.8.6: 108 req/sec
1.9.0: 126 req/sec

次に

rake db:fixtures:load

として record を 2 個置いた (genre は null)。

----------------------------------------
Time: Sat, Mar 10 2007 16:21:56 +0900
Ruby version: 1.8.6
                                      user     system      total        real
load environment                  0.740000   0.090000   0.830000 (  0.825608)
/musics/index                     1.040000   0.130000   1.170000 (  1.173964)
----------------------------------------
Time: 2007-03-10 16:20:53 +0900
Ruby version: 1.9.0
                                      user     system      total        real
load environment                  1.100000   0.120000   1.220000 (  1.216180)
/musics/index                     0.840000   0.110000   0.950000 (  1.039765)
1.8.6: 85.2 req/sec
1.9.0: 96.2 req/sec

_ KaracriBoard

成型機監視にエスアイ創房さんの KaracriBoard を購入。

# u.rb:
require 'socket'
socket = UDPSocket.new
socket.connect('192.168.0.200', 20000)
while request = gets.chomp
  socket.send(request, 0)
  begin
    response, addr = socket.recvfrom(128)
    p response
  rescue
    p $!
  end
end
$ ruby u.rb
1 hello
"1 HELLO TK0040A v1.00 MyCpuName 192.168.0.200 0004b9xxxxxx H 1234.000"
1 din
"1 DIN 000000 0000"
1 din
"1 DIN 100000 0000"
1 ain
"1 AIN 1023 1023 1023 1023 0 0"
1 ain
"1 AIN 1023 1023 1023 2 0 0"

楽しす。


2007-03-12 [長年日記]

_ [Ruby] ruby-1.9 profiler

profiler ほしい!

_ register calling convention

Delphi の register calling convention を Ruby/DL で扱いたいという話なのだけど難しいよね。

_ [Rails] benchmark: activesupport が怪しい

先日の benchmark

# sqlite3-ruby select (n=1000)
ruby-1.8.6: 2142 req/sec
ruby-1.8.6: 2153 req/sec
ruby-1.9.0: 2313 req/sec
ruby-1.9.0: 2328 req/sec
# activerecord -> sqlite3-ruby select (n=1000)
ruby-1.8.6: 1098 req/sec
ruby-1.8.6: 1103 req/sec
ruby-1.9.0:  801 req/sec
ruby-1.9.0:  806 req/sec

と興味深い結果を得たのだけど、どうも activesupport が怪しい。

# sqlite3-ruby select (n=1000) with require 'active_record'
ruby-1.8.6: 1936 req/sec
ruby-1.8.6: 1931 req/sec
ruby-1.9.0: 1550 req/sec
ruby-1.9.0: 1565 req/sec

require するだけでこのありさま。

vendor/builder/blankslate.rb で Kernel.method_added Object.method_added を書き換えてるのが効いてるのかも。

少なくとも特化命令は有効。

module Builder
  BlankSlate = ::BasicObject

にしてみたけど、変わらないなあ。なんだろう。

計測の結果、次のライブラリを require すると速度低下が顕著。

  • builder
  • core_ext
  • dependencies
  • reloadable
  • deprecation
  • multibyte

ってほとんど全部なんだけど。

でも benchmark に影響するのはなんでかなあ。 Benchmark.measure 自体が影響を受けているのだろうか。

# benchmarks/bm3.rb:
require 'benchmark'

RUNS = 1000

require 'sqlite3'

$:.unshift(File.dirname(__FILE__) + '/../lib')
require 'active_record'

$VERBOSE = nil

File.delete "test3.db" rescue nil
db3 = SQLite3::Database.open( "test3.db" )
db3.execute "create table foo (a,b)"
db3.execute "insert into foo values (1,2)"
db3.execute "insert into foo values (3,4)"
db3.execute "insert into foo values (5,6)"

puts
puts "queries"

runtime = Benchmark.measure do |x|
  RUNS.times do
    db3.execute "select * from foo"
  end
end

puts "Rehearsal"
puts "Runs: #{RUNS}"
puts "Avg. runtime: #{runtime.real / RUNS}"
puts "Requests/second: #{RUNS / runtime.real}"

runtime = Benchmark.measure do |x|
  RUNS.times do
    db3.execute "select * from foo"
  end
end

puts "Runs: #{RUNS}"
puts "Avg. runtime: #{runtime.real / RUNS}"
puts "Requests/second: #{RUNS / runtime.real}"

db3.close

_ 青画面

 IRQL_NOT_LESS_OR_EQUAL

 STOP: 0x0000000A (0XCDCDCD,0x00000002,0x00000001,0x804DDC9A)

これに引っかかったのかなあ。

_ sqlite3-ruby benchmark

sqlite3-ruby-bench-1.jpg

bm3: sqlite3-ruby のみ
bm3as: require 'active_record' あり

2007-03-15 [長年日記]

_ require "active_record" benchmark

結果:

bm-186.gif

bm-190.gif

GC が効いていることはわかった。しかし 1.9 では線形に見える。


2007-03-18 [長年日記]

_ Puppet

OSC2007 (1日目) で聴講した 16-B2B-4 オープンソースによるシステム管理の自動化。 ぱぺっと。Rubyで書かれていてむちゃ流行ってるらしい。良さそう。けどストヤンは知らなかった。

こういうのは何ツールて言うんだろう。

Puppet is a system configuration tool.
Puppet is an open-source next-generation server automation tool.

_ Hinemos

OSC2007 (1日目) で聴講した 16-7A-5 Hinemosでできる!今日から始める運用管理。 ひねもす。未だMRTGな人なのでNagiosとか使えるようになりたいと思っていた。興味深い。

_ [Rails] int.jitor.net

これは流行らせたい。:)

Entertainment Rails!

Ruby APIもある。

_ [Rails] Rails 勉強会@関西 第7回

Rails for Ruby 1.9。一般受けしなさそうなネタをどうしようかと思って、ユーザから見て気になるであろう Ruby 1.9 の変更点を紹介してみた。 時間が余らなくて良かった。 発表資料。 Ruby 1.9 の売りは速度のほか何があるだろう。 組み込み breakpoint があると嬉しいかも。 rake が動かないバグは GC.stress で見つかるかもしれない (znz さんの示唆)。

氏久さんの int service は夢が広がりんぐ (上記 int.jitor.net)。

モバイル通信兵さんの発表は実用度が高い。 BackgrounDRb と組み合わせたい。

_ [Ruby] tDiary on Ruby 1.9, Hiki on Ruby 1.9

雪見酒さんが対応されたようです。Great work!


2007-03-25 [長年日記]

_ [] 市立図書館

DB勉強会参加したい! てことでRDBMSの基礎を学ぶ一冊。

データベースパフォーマンスアップの教科書 基本原理編 データベースパフォーマンスアップの教科書 基本原理編

比喩が楽しい。 こんなところに萌え要素。

闘うのはオプティマイザだが、武器はユーザが事前に提供しなくてはならない。……現実には、ほとんどのユーザは自分が武器を提供しなかったにも関わらず、戦いに敗れた者だけを責めている。オプティマイザが喋れるなら、さぞかし不満を述べるに違いない。(p.83)

オプティマイザ萌えー。

今、我々は教師としてSQLを通してオプティマイザに宿題を出すのである。……しかし現実はどうであろうか? 立場は教師であるが、それにふさわしい行動をとっている人はほとんどいないのではないだろうか。あまりに多くの宿題を出すと、学生が辛いのではと心配していないだろうか? データベースが可哀想に思えて、自分が直接宿題をしていないだろうか?(p.84)

_ [Rails] BackgrounDRb

BackgrounDRb いいね。win32 でも動くし。 単純な fork を実現する定石ぽい手順を紹介する。

時間のかかる処理を行うアクションがあるとする。 この処理をユーザが待つ必要はなく、バックグラウンド処理しても構わない類のものとする。

# app/controllers/board_controller.rb:
  def post
    @school = School.find(params[:id])
    # ここで時間のかかる処理を行う...
    redirect_to :action => 'index', :id => @school
  end

時間のかかる処理を BackgrounDRb のワーカスレッドに置くことができる。

# app/controllers/board_controller.rb:
  def post
    @school = School.find(params[:id])
    # worker を作ってすぐ戻る
    MiddleMan.new_worker(:class => :post_worker, :args => { :id => @school.id })
    redirect_to :action => 'index', :id => @school
  end
# lib/workers/post_worker.rb:
class PostWorker < BackgrounDRb::Rails

  def do_work(args)
   @school = School.find(args[:id])
   # ここで時間のかかる処理を行う...
  ensure
    kill()
  end
end

処理コードを切り貼りするだけである。 ワーカスレッドでの ActiveRecord の呼び出しも問題ない。

対応する test も分離しよう。 functional test での MiddleMan は mock で置き換える。

# test/mocks/test/backgroundrb.rb:
class MiddleMan
  def self.new_worker(opts={})
    'dummy'
  end

  def self.get_worker(key)
    'dummy'
  end

  def self.delete_worker(key)
    true
  end
end

これは Rails 流の mock 実現法であり、 test/mocks/ が vendor/ より先に探索されることを利用している。

# vendor/plugins/backgroundrb/init.rb:
require 'backgroundrb'

test 環境では この require で test/mocks/test/backgroundrb.rb が読み込まれるわけだ。

do_work に移した処理に対応する test コードを worker test に移す。

# test/functional/board_controller_test.rb:
  def test_post
    room = rooms(:fuzoku_school)
    post 'post', :id => room.id
    assert_response :redirect
    assert_redirected_to :action => 'index'
    #assert_equal 1, ActionMailer::Base.deliveries.size
  end
# test/unit/post_worker_test.rb:
class PostWorkerTest < Test::Unit::TestCase
  fixtures :rooms

  def setup
    @middleman = BackgrounDRb::MiddleMan.instance
    @middleman.set_sleep  0.05
    @middleman.start_timer
    @middleman.gc! Time.now
  end

  def test_post
    room = rooms(:fuzoku_school)
    job_key = @middleman.new_worker(:class => :post_worker, :args => { :id => room.id })
    sleep 0.1 while @middleman.get_worker(job_key)
    # 以降に do_work の test を記述
    assert_equal 1, ActionMailer::Base.deliveries.size
  end
end

ここでの setup は BackgrounDRb 自体の test を参考にした。 kill() されると @middleman.get_worker が nil になるので、それまで sleep して待つ。 test では do_work(args) を直接呼んでもいいかもしれない。

本日のツッコミ(全1件) [ツッコミを入れる]

# shachi [後半はrailsの話ばっかりでしたが>DB勉強会 来月もやるようなので次回はぜひ。]


2007-03-30 [長年日記]

_ BGM Driven Development Environment

ゲーム脳+TDD

red/green bar を聴覚変換してみる。

  1. (テスト書き)
  2. Red Bar: BGM=バトル
  3. (実装)
  4. Green Bar: BGM=フィールド
  5. (リファクタリング)

autotest だとフック書けるから簡単。


2007-03-31 [長年日記]

_ [Apollo] EInvalidPointer on ruby-1.8.6

require 'phi'
Phi::Point.new(1, 2)
ruby 1.8.5 (2007-03-13 patchlevel 35) [i386-mswin32]
no error
ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]
EInvalidPointer

うー

_ [Apollo] わかりました

Turbo Delphi Explorer でデバッグ。

@DynArrayClear

なるほど。動的配列の解放でこける。

function Phi_new_item(argc: integer; argv: Pointer; This: Tvalue): Tvalue; cdecl;
var
  args: array of Tvalue;
begin
  SetLength(args, argc);
  args := argv;
  acap := dl_String(args[0]);

だとまずい。

function Phi_new_item(argc: integer; argv: Pointer; This: Tvalue): Tvalue; cdecl;
var
  args: Pvalue;
begin
  args := argv;
  acap := dl_String(args^); inc(args); dec(argc);

ださいなあ。