2015/07/28


Ruby 製バッチ処理を省メモリ化した - 彼女からは、おいちゃんと呼ばれています

少し前に Ruby 製の バッチ処理 を省メモリ化したときの話をメモしておきます。 どのような バッチ処理 だったか 動画共有サイト にアップされた動画がオトナの事情によって削除されることがしばしばあ...

http://blog.inouetakuya.info/entry/2015/07/26/204320

ActiveRecord の each は全件、find_each はデフォルトで1000件をインスタンス化するので、HTML をパースしたDOMツリーが1000件もメモリに乗ったらそりゃ OOM Killer で殺されても文句は言えないかもしれない。

ActiveRecord::Batches

The find_each method uses find_in_batches with a batch size of 1000 (or as specified by the :batch_size option).

http://api.rubyonrails.org/classes/ActiveRecord/Batches.html

試しにキャッシュと呼んでおられる @doc の中身を表示すると

  def doc
    p @doc
    # 動画配信元ページの HTML をキャッシュ
    @doc ||= Nokogiri::HTML(open(source_url))
  end
#<Nokogiri::HTML::Document:0x149ddbc name="document" children...
#<Nokogiri::HTML::Document:0x11abd6c name="document" children...
#<Nokogiri::HTML::Document:0xdbcd8c name="document" children=...

毎回異なるメモリのアドレス値が表示されており全くキャッシュに意味が無い事が分かる。上記の通り find_each の仕様ならばきちんと1000個の DOM ツリーがメモリに乗っている事になる。

class Video < ActiveRecord::Base
  after_initialize do
    doc ||= Nokogiri::HTML(open(source_url))
    @deleted = doc.at_css('.message').try(:text).try(:match'This video has been deleted')
    @removed = doc.title.match 'Removed'
  end

  def deleted?
    match_removed? || match_deleted?
  end

  def existing?
    !deleted?
  end

  private

  def match_deleted?
    @deleted
  end

  def match_removed?
    @removed
  end
end

こんな感じに使ったらすぐに doc を捨ててしまうか、batch_size の数を減らして一度にインスタンス化される数を制限する事で解決しそうな気がします(こちらは未検証)。

Video.find_each(batch_size5do |video|
  video.delete! unless video.existing?
end
改訂3版基礎 Ruby on Rails (KS IMPRESS KISO SERIES) 改訂3版基礎 Ruby on Rails (KS IMPRESS KISO SERIES)
黒田 努
インプレス / ¥ 3,672 (2015-05-22)
 
発送可能時間:在庫あり。


2015/07/03


ちょっと書き物をする用事があって、markdown で書かなきゃいけなかったんですが

  • 余計なインストールの手間が不要で
  • 余計な設定は一切不要で
  • 使い勝手は Linux や Windows 全て同じで
  • 出来れば Markdown 保存したら自動更新されて
  • GitHub Flavour Markdown でコードハイライトト
  • GitHub Flavour Markdown でテーブル表記

こういうのが欲しくて、あるアプリケーションを見つけたんだけど外部の JavaScript を使ってる上にそのリソースURLが無くなり動かなくなってて困ったので作った。

今後もこういう事あるだろうし、自分用に作っておくべきだと思った。書き物をする予定だったのに気付いたら作ってたのでしょうがない。僕のせいではない。

mattn/mkup - GitHub

README.md mkup Portable Markdown Previweer Usage 1 mkup Installation 1 go get github.com/mattn/mkup ...

https://github.com/mattn/mkup
$ mkup

でカレントディレクトリ配下のファイルがサーブされます。.md, .mkd, .markdown の拡張子が付いたファイルは GitHub Flavour Markdown で綺麗に表示されます。ただし GitHub の API は使いません。ブラウザで http://localhost:8000 を開いておけばファイル更新後に勝手にリロードされます。テーブルタグもサポートしていますし、コードハイライトもサポートしています。ただし Google Code Prettify なので誤爆はあり得ます。

尚、mkup は JavaScript や CSS といった全てのリソースを実行モジュール内に保持していますので、Windows であれば exe ファイル1個あればどこでもプレビュー出来ますし、ちょっと別のPCで作業する必要があるって時にも exe ファイルだけコピーすればすぐ Markdown を書きが始められます。

自分専用なので最少の機能しかないですが、よろしければどうぞ。リリースにバイナリが置いてあるので持ってってください。

Releases - mattn/mkup - GitHub
https://github.com/mattn/mkup/releases

2015/06/18


Matz も驚く Crystal


Crystal

Language Goals Ruby-inspired syntax. Statically type-checked but without having to specify the type ...

http://crystal-lang.org/

ruby と殆ど同じシンタックスが通る様です。

# A very basic HTTP server
require "http/server"

server = HTTP::Server.new(8080do |request|
  HTTP::Response.ok "text/plain""Hello world!"
end

puts "Listening on http://0.0.0.0:8080"
server.listen

しかも native code が吐けるらしい。ならばとベンチマークを取ってみた。CRuby 側は以下の sinatra。CRuby は 2.2.0p0 を使用。

require "sinatra"

set :environment:production

get "/" do
  content_type 'text/plain'
  "Hello world!"
end
$ ab -k -c 10 -n 10000 http://localhost:8000

まずは sinatra

Server Hostname:        localhost
Server Port:            4567

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      10
Time taken for tests:   8.502 seconds
Complete requests:      10000
Failed requests:        0
Keep-Alive requests:    10000
Total transferred:      1620000 bytes
HTML transferred:       120000 bytes
Requests per second:    1176.24 [#/sec] (mean)
Time per request:       8.502 [ms] (mean)
Time per request:       0.850 [ms] (mean, across all concurrent requests)
Transfer rate:          186.08 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     1    8   2.6      8      66
Waiting:        1    8   2.6      8      66
Total:          1    8   2.6      8      67

Percentage of the requests served within a certain time (ms)
  50%      8
  66%      9
  75%      9
  80%     10
  90%     11
  95%     13
  98%     14
  99%     16
 100%     67 (longest request)

そして Crystal

追記: 以下はデバッグビルドの結果でした。その下に続けてリリースビルド時の結果を追記しています。

デバッグビルド

Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      10
Time taken for tests:   0.633 seconds
Complete requests:      10000
Failed requests:        0
Keep-Alive requests:    10000
Total transferred:      1010000 bytes
HTML transferred:       120000 bytes
Requests per second:    15802.98 [#/sec] (mean)
Time per request:       0.633 [ms] (mean)
Time per request:       0.063 [ms] (mean, across all concurrent requests)
Transfer rate:          1558.69 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     0    1   0.2      1       3
Waiting:        0    1   0.2      1       3
Total:          0    1   0.2      1       3

Percentage of the requests served within a certain time (ms)
  50%      1
  66%      1
  75%      1
  80%      1
  90%      1
  95%      1
  98%      1
  99%      1
 100%      3 (longest request)

リリースビルド

Server Software:        
Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      10
Time taken for tests:   0.295 seconds
Complete requests:      10000
Failed requests:        0
Keep-Alive requests:    10000
Total transferred:      1010000 bytes
HTML transferred:       120000 bytes
Requests per second:    33888.54 [#/sec] (mean)
Time per request:       0.295 [ms] (mean)
Time per request:       0.030 [ms] (mean, across all concurrent requests)
Transfer rate:          3342.52 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     0    0   0.1      0       1
Waiting:        0    0   0.1      0       1
Total:          0    0   0.1      0       1

Percentage of the requests served within a certain time (ms)
  50%      0
  66%      0
  75%      0
  80%      0
  90%      1
  95%      1
  98%      1
  99%      1
 100%      1 (longest request)

比べ物にならなかった。ただ、h2ohttp-server だとファイルをサーブしたとしても 28000req/sec から 30000req/sec は出る環境なので、Crystal のネイティブコードがめちゃくちゃ速いという訳ではない。