转载

Ruby Profiler详解之stackprof

简介

stackprof是基于采样的一个调优工具,采样有什么好处呢?好处就是你可以线上使用,按照内置的算法抓取一部分数据,只影响一小部分性能。它会产生一系列的dump文件,然后你在线下分析这些文件,从而定位出问题,google有一篇基于采样的论文,也基本证明了采样是可行的。而stackprof也是深受google的perftools的影响,采用了采样的方式来做调优。

基本使用方法

StackProf.run(mode: :cpu, out: './stackprof.dump') do   # 你的代码 end 

这里我们给出一段示例代码,来作为测试目标:

require "stackprof"  class Compute    def m1     "string" * 100   end    def m2     "string" * 10000   end    def start     100_000.times do       m1       m2     end   end end  StackProf.run(mode: :cpu, out: './stackprof.dump') do   Compute.new.start end 

保存为test.rb,同时执行 ruby test.rb 就会在当前目录下生成stackprof.dump文件,我们用stackprof打开这个文件:

stackprof stackprof.dump --text 
==================================   Mode: cpu(1000)   Samples: 1793 (0.61% miss rate)   GC: 587 (32.74%) ==================================      TOTAL    (pct)     SAMPLES    (pct)     FRAME       1106  (61.7%)        1106  (61.7%)     Compute#m2         98   (5.5%)          98   (5.5%)     Compute#m1       1206  (67.3%)           2   (0.1%)     block in Compute#start       1206  (67.3%)           0   (0.0%)     <main>       1206  (67.3%)           0   (0.0%)     Compute#start       1206  (67.3%)           0   (0.0%)     <main>       1206  (67.3%)           0   (0.0%)     block in <main> 

这里可以很明显的看出是m2方法比较慢,占据了大部分的执行时间,相比其他的调优工具,它只是列出了用户自己的方法所占时间比,在ruby-prof中的测试中,它是会显示 String#* 这个方法的占比的,但是对于我们来说,它的意义不大,而stackprof是不会理会标准库里的方法的。同时stackprof也是可以过滤方法的,比如我们发现了m2这个方法有问题,那么就可以把它过滤出来,看看细节:

stackprof stackprof.dump --text --method 'Compute#m2'  Compute#m2 (/Users/lizhe/Workspace/ruby-performance-tuning/test.rb:9)   samples:  1106 self (61.7%)  /   1106 total (61.7%)   callers:     1106  (  100.0%)  block in Compute#start   code:                                   |     9  |   end  1106   (61.7%) /  1106  (61.7%)  |    10  |                                   |    11  |   def start 

我们可以看到m2这个方法定义在哪一个文件的哪一行,同时是谁调用了它,以及还显示了它在源码中的上下文。假如有多个方法调用了m2,还会显示出这几个方法,以及他们调用m2所占的比例,也就是上面的callers部分,因为只有一个start方法调用了m2,所以它是100%。

在rack中的使用方法

stackprof本身实现了一个rack middleware,所以可以很方便的挂载到一个rack应用中:

use StackProf::Middleware, enabled: true, mode: :cpu, save_every: 5 

在rails中使用,先在Gemfile中添加stackprof,然后添加middleware:

config.middleware.use StackProf::Middleware, enabled: true, mode: :cpu, save_every: 5 

然后请求你的应用,多请求几次,每5秒钟它会保存一次输出结果到tmp目录中,查看其中某一个结果:

==================================   Mode: cpu(1000)   Samples: 155 (0.00% miss rate)   GC: 11 (7.10%) ==================================      TOTAL    (pct)     SAMPLES    (pct)     FRAME         18  (11.6%)          18  (11.6%)     Hike::Index#entries         12   (7.7%)          12   (7.7%)     Hike::Index#stat          9   (5.8%)           9   (5.8%)     #<Module:0x007fb72a0c7b08>.load_with_autoloading         18  (11.6%)           9   (5.8%)     Sprockets::Cache::FileStore#[]          6   (3.9%)           6   (3.9%)     block (2 levels) in BindingOfCaller::BindingExtensions#callers          5   (3.2%)           5   (3.2%)     Time.parse          5   (3.2%)           5   (3.2%)     Sprockets::Mime#mime_types          5   (3.2%)           5   (3.2%)     Pathname#chop_basename          4   (2.6%)           4   (2.6%)     block in ActionView::PathResolver#find_template_paths          4   (2.6%)           4   (2.6%)     block in BetterErrors::ExceptionExtension#set_backtrace         15   (9.7%)           3   (1.9%)     block in ActiveSupport::Dependencies#load_file          2   (1.3%)           2   (1.3%)     Temple::Mixins::CompiledDispatcher::DispatchNode#initialize          5   (3.2%)           2   (1.3%)     ActionDispatch::Cookies::EncryptedCookieJar#initialize          2   (1.3%)           2   (1.3%)     ActiveSupport::KeyGenerator#generate_key          2   (1.3%)           2   (1.3%)     block in ActionView::PathResolver#query          4   (2.6%)           2   (1.3%)     Slim::Parser#initialize        113  (72.9%)           2   (1.3%)     ActionView::Renderer#render_template          2   (1.3%)           2   (1.3%)     Hike::Trail#stat          2   (1.3%)           2   (1.3%)     block in ActiveSupport::Dependencies#search_for_file         22  (14.2%)           2   (1.3%)     block in Temple::Filters::MultiFlattener#on_multi         20  (12.9%)           2   (1.3%)     Temple::Filters::ControlFlow#dispatcher         15   (9.7%)           2   (1.3%)     ActionView::Renderer#render_partial          1   (0.6%)           1   (0.6%)     block in Slim::Parser#initialize          1   (0.6%)           1   (0.6%)     Pathname#prepend_prefix          1   (0.6%)           1   (0.6%)     String#blank?          1   (0.6%)           1   (0.6%)     ActiveSupport::SafeBuffer#initialize         10   (6.5%)           1   (0.6%)     Sprockets::Asset#dependency_fresh?          1   (0.6%)           1   (0.6%)     Sprockets::Asset#init_with          1   (0.6%)           1   (0.6%)     Hike::Index#sort_matches          1   (0.6%)           1   (0.6%)     block in ActiveSupport::Dependencies::Loadable#require 

可以利用这样的方式调试线上的环境。

参考链接:

https://github.com/tmm1/stackprof

本文由OneAPM工程师原创,欢迎大家来OneAPM做客,共同讨论各种技术问题

正文到此结束
Loading...