09 March 2015

前言

使用ruby-prof评估一下,某些代码执行的性能,对比一下使用那种方式好。

使用

使用ruby-prof的API,其使用的评估通用函数如下:

def prof
  result = RubyProf.profile do 
    yield
  end
  
  printer = RubyProf::FlatPrinter.new result
  printer.print STDOUT, {} 
end

备注: 输出实在太吓人了,Mongoid的调用关系。

测试代码

以下是一些代码的测试和结果:

第一组代码:

# 测试代码
prof do 
  $redis.hgetall("stocks").values
end 
# 测试结果,仅有等待时间
Measure Mode: wall_time
Thread ID: 12111860
Fiber ID: 57817860
Total: 0.183511
Sort by: self_time

第二组代码:

# 测试代码
prof do 
  Stock.only(:chinese_name).map { |x|  Regexp.escape(x.chinese_name) }
end
# 测试结果,仅有等待时间
Measure Mode: wall_time
Thread ID: 12111860
Fiber ID: 57817860
Total: 0.822610
Sort by: self_time

prof do Stock.only(:chinese_name).map(:chinese_name) end

可以看出,$redis确实快那么一点。

# 测试代码 
prof do 
  $redis.mapped_hmset("stocks",Stock.only(:chinese_name).map { |x| [ x.id ,  { id:  x.id,  chinese_name: x.chinese_name} ] })
  $redis.hgetall("stocks").values
end
# 测量结果
Measure Mode: wall_time
Thread ID: 12111860
Fiber ID: 57817860
Total: 1.242096
Sort by: self_time

对比代码实现相同功能的代码,猜猜性能如何:

# 实现1
Article.limit(2).each do |article|
  stock_list = article.content.scan(re)
  next if stock_list.blank?
  article.stocks = Stock.where(:chinese_name.in => stock_list)
  article.save
end
# 实现2
Article.limit(2).each do |article|
  article.content.scan(re).each do |x|
    st = Stock.where(chinese_name: x).first
    article.stocks << st
  end
  article.save
end

实际上,从代码上来看,似乎实现1要比实现而优雅。使用ruby-prof测试结果如下:

# 实现1
Measure Mode: wall_time
Thread ID: 4317180
Fiber ID: 53636680
Total: 13.684149
Sort by: self_time
# 实现2
Measure Mode: wall_time
Thread ID: 4317180
Fiber ID: 53636680
Total: 1.689249
Sort by: self_time

实现1居然要花6-7秒处理一篇文章,29万篇就要20天,天啊,坑爹啊。幸亏有性能探测工具,要不然,鬼知道问题 出在哪里。不过,看到结果后,可以发现,SQL的in语句的查询比较时,且不能使用索引。

后记

性能监测工具真是个好东西,有了它,就知道代码执行的性能如何了。Newlic的性能监控工具更好,据说是实时的,虽然稍有延迟,我要将其用在mobile项目中。




傲娇的使用Disqus