ruby-prof实战
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