Rubyでマルチコアプログラミング!

一眼レフを買ってから写真を撮りまくってるonodesです.
いつも写真をネット上に公開するのにフォト蔵Flickr(パス忘れて使えない)、zenphoto(
家のサーバ)を使っています.

旧サーバ(シングルコア)

zenphotoはPHPで書かれているwebアルバムソフトなんですが、サムネイルを動的に生成します.1回目は動的、二回目以降はサムネをキャッシュしてそこから呼び出す形を取るんですが・・・このzenphotoのキャッシュを作るには2つ方法があって

  • 全部のページを開く
  • ブラウザから管理なのでブラウザ管理画面を開いてそこのキャッシュを作る(ブラウザつけっぱ)

の二種類です.

常にブラウザつけてるのもあれですし、PHPの最大応答時間に引っかかることもある・・・のかな?

なのでいつものRubyを使ってzenphotoのサムネを生成してみました.

zenphotoでは
/zenphoro/albums/albumに写真が保存されます.キャッシュは
/zenphoto/cache/albumです.

つまり
albumsの写真をサムネ化してcacheに保存してやれば良い.
そしてサムネ写真は二種類必要です.

今回のプログラムは作成先のフォルダが作られていること、設定が初期状態であることの元作成しました.

require 'rubygems'
gem PLATFORM =='java'? 'rmagick4j' : 'rmagick'
require 'RMagick'
#require 'magickwand'
dirname = ARGV.to_s.chomp



def run_gc
#	fDisabled = GC.enable
#	GC.start
#	GC.disable if fDisabled
end


open(dirname+"index.html","a").close
#dir = Dir.open(dirname)

i = 0

infile = []
new_dir = dirname.sub('albums','cache')
#fp = popen("mkdir "+newdir)
open(new_dir+"index.html","a").close
dir = Dir.open(new_dir)
ar_file_count = 0
new_file_count = 0

dir.each{|filename|
	next if filename =='index.html' || filename == '.' || filename == '..' || !(filename.to_s.include?('thumb.jpg'))
	infile<<filename.to_s[0,8] + '.jpg'

}
p infile.sort


dir = Dir.open(dirname)
count = 0
dir.each{|filename|
   next if filename=='.' || filename =='..' || filename == 'index.html'
   count += 1
}

dir.each{ |filename|
  
  next if filename == '.' || filename == '..' || filename == 'index.html'
puts filename 
 if infile.include?(filename.to_s)
	puts 'fileOK!'
	next
  end	
  puts '----------------'
  puts filename + ' => start'
  
  f1 = File.basename(filename) + '_100_cw85_ch85_thumb.jpg'
  f2 = File.basename(filename) + '_595.jpg'

  puts new_dir
  new_filename1 = new_dir + f1
  new_filename2 = new_dir + f2
  
 img = Magick::Image::read(dirname + filename).first
  
  new_img1 = img.resize(85,85)
  new_img1.write(new_filename1)
#IO.popen("convert -resize 80x80 " + dirname+filename +" " + new_filename1 ) 
 
  puts 'create => ' + f1

  
  new_img2 = img.resize(595,398)
  new_img2.write(new_filename2)
  puts 'create => ' + f2
#IO.popen("convert -resize 595x398 " + dirname+filename +" " + new_filename2 ) 

run_gc
  #puts
  #puts i.to_s + " / " + count.to_s
  #puts ((i/count.to_f)*100).to_s + "%"
  #i+=1
   

}

puts 'end and ' + i.to_s +  ' files'


コメントアウトされてますが!

def run_gc
#	fDisabled = GC.enable
#	GC.start
#	GC.disable if fDisabled
end

このRubyImageMagickのRMagickというものにはバグがあり、メモリがリークしてしまいます.その結果としてRubyGCを手動で動かしています.

新サーバ

onodes新サーバは仮想クアッドコアなのでマルチスレッドでこのプログラムを動作させたいわけです.Ruby1.8、Ruby1.9ともにネイティブスレッドには対応していないのでJavaVMが使えるjRubyでマルチスレッド対応にしたいと思います.

急いで作ったのでいいソースではないですがgithubにおいてあります.
http://github.com/onodes/thumbrb

#start.rb
#jruby start.rb /path
require 'rubygems'
gem PLATFORM =='java'?'rmagick4j' : 'rmagick'
require'RMagick'
require'thumb'
require 'pp'
require 'thread'

q = Queue.new

dir_r = ARGV.to_s.chomp
dir_w = dir_r.gsub("albums","cache")


ary = Dir::glob(dir_r + "*.{jpg,JPG}").map{|filename| Thumb.new(filename)}
puts
puts
puts "-"*30
puts "-"*3 + "For zenphoto On PHP Web Album" + "-"*3
puts "Created by onodes 2009-2010"
puts
puts "-"*30

sleep 1.5


ary.each{|r|
  q.push(r)
}

t1 = Thread.new{
  while(!q.empty?)
    print "Thread1 : "
    instance = q.pop
    instance.convert
  end
self.kill
}

t2 = Thread.new{
  while(!q.empty?)
    print "Thread2 : "
    instance = q.pop
    instance.convert
  end
self.kill
}

t3 = Thread.new{
  while(!q.empty?)
    print "Thread3 : "
    instance = q.pop
    instance.convert
  end
self.kill
}

t1.join
t2.join
t3.join

puts "End"
#thumb.rb
require 'rubygems'
gem PLATFORM == 'java'? 'rmagick4j':'rmagick'
require 'RMagick'

class Thumb

  def initialize(path_r,opt='')
    @path_r = path_r
    @path_w = path_r.gsub("albums","cache")
  end

  def rename(add)
    dirname + '/' + filename + add + extname
  end


  def convert
    img = Magick::Image::read(@path_r).first
    img.resize(85,85).write(rename('_100_cw85_ch85_thumb'))
    img.resize(595,398).write(rename('_595'))

    puts filename+"created"
  end

  private
  def extname
    @extname ||= File.extname(@path_w)
  end

  def filename
    @filename ||= File.split(@path_w)[1].gsub(extname,"")
  end

  def dirname
    @dirname ||= File.dirname(@path_w)
  end
end

これで

jruby start.rb /写真ファイルのディレクトリ名

とやると
そのフォルダ内の(今はjpg)のファイルのサムネが作られます.