DIY Ruby CPU 分析 Part II

【编者按】作者 Emil Soman,Rubyist,除此之外竟然同时也是艺术家,吉他手,Garden City RubyConf 组织者。本文是 DIY Ruby CPU Profiling 的第二部分。本文系 OneAPM 工程师编译整理。

DIY Ruby CPU 分析——Part I
DIY Ruby CPU 分析——Part I

在第一部分中我们学习了 CPU 分析的含义和进行 CPU 分析的两种方法,点此处回顾第一篇精彩内容。在这一部分我们将研究 CPU time 和 Wall time,这些部分总被用来测量执行开销。我们也会写一些实现这些测量方法的代码作为建立 CPU 分析的第一步。

Part II. CPU time 和 Wall time

Wall time

Wall time 是在一个方法被调用和返回之间的真实时间。因此,如果你想要测量一个方法执行的 「Wall clock time」,理论上可以用秒表来测量。只要在方法开始执行时打开秒表,在方法返回时按下停止。这个时间通常也被称为真实时间。

关于 Wall time 很重要的一点是,可以预见,每次试图测量同一段代码可能得到不同的结果。这是因为一系列后台进程会影响 Wall time. 当 CPU 同时运行多个进程的时候,操作系统给同时运行的进程排期并且试图为它们公平的分配 CPU 空间。这意味着 CPU 花费的总时间被分成多个片而我们的方法只占用其中的一些时间片。因此,当 Wall clock 开始计时,我们的进程可能会闲置并且为并行运行的其他进程让路。这意味着花费在其他进程的时间将增加我们的 Wall time!

CPU time

CPU time 是指 CPU 执行方法的时间。CPU time 的度量单位是用于执行方法的 CPU 时钟周期。如果我们了解 CPU 频率,它的单位是周期每秒,也可以称作赫兹,那么我们可以将其转换为时间。如果 CPU 执行某一方法花了 x 个时钟周期,这个 CPU 频率是 y 赫兹,那么 CPU 执行方法的时间为 x/y 秒。有时操作系统会为我们自动进行转换从而使我们免于进行这种计算。
CPU 时间不等同于 Wall time,其中的差别在于方法的指令类型。我们可以宽泛的将指令分为两种类型:CPU 密集型 和 I/O 密集型. 在执行 I/O 指令时,CPU 空闲下来可以转去执行其他 CPU 密集型指令。因此,如果我们的方法在 I/O 指令上花费时间,CPU 可以不把时间投入在该方法上,而是去处理其他事情,直到 I/O 操作完成。 这段时间内 Wall time 在计时而 CPU time 停止计时,落后于 Wall time.

我们来看看一个需要5分钟来执行的慢方法的情况。如果想知道这个方法花费了多长时间,你的 Wall clock 可以显示「执行该方法需要五分钟」,但 CPU 会显示「执行该方法中用时 3 分钟」。所以应该听从哪一个说法呢?究竟哪个时间能够更准确的测量执行方法的时间?

答案是:看情况。这取决于你希望测量的方法的类型。如果该方法的大部分时间用于 I/O 操作,或者该方法没有直接处理 CPU 密集型指令,由 CPU time 描述的时间开销将十分不准确。对于这些类型的方法,通过 Wall time 来测量时间更加合适。而对于其他情况,坚持通过 CPU time 来测量是很可靠的。

测量 CPU time 和 Wall time

鉴于想要写一个 CPU 分析器,我们需要一种测量 CPU time 和 Wall time 的方法。下面来看一看已经能够测量这两项的 Ruby 的 Benchmark module 中的代码。

def measure(label = "") # :yield:
  t0, r0 = Process.times, Process.clock_gettime(BENCHMARK_CLOCK)
  yield
  t1, r1 = Process.times, Process.clock_gettime(BENCHMARK_CLOCK)
  Benchmark::Tms.new(t1.utime  - t0.utime,
                     t1.stime  - t0.stime,
                     t1.cutime - t0.cutime,
                     t1.cstime - t0.cstime,
                     r1 - r0,
                     label)
end

由此可见,Ruby 通过两种进程类中的方法来测量时间:

  1. 通过times测量 CPU time.
  2. 通过clock_gettime来测量真实时间,也就是 Wall time.
    但是times方法返回的结果为1秒,这表示通过分析器用times只能测量仅需要几秒就能完成的 方法的 CPU time. 然而clock_gettime就有趣多了。

clock_gettime

Process::clock_gettime是早在 Ruby 2.1 版本就已经被添加的方法,它使用 POSIX clock_gettime()功能并回退到 OS 仿真来获得时间以防clock_gettime在 OS 中失效或无法实施。该功能接受clock_id及时间结果作为参数。有很多可以被选为这种计时器的clock_ids,但我们感兴趣的是:

  1. CLOCK_MONOTONIC: 这个计时器测量逃走的 Wall clock time,因为过去的任意时间点不会被系统时钟的变化影响,最适合测量 Wall time.
  2. CLOCK_PROCESS_CUPTIME_ID: 这个计时器测量每一个进程的 CPU time,意即计算进程中所有线程的时间。我们可以用它来测量 CPU time.
    让我们利用这个来写一些代码:
module DiyProf
  # These methods make use of `clock_gettime` method introduced in Ruby 2.1
  # to measure CPU time and Wall clock time.

  def self.cpu_time
    Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :microsecond)
  end

  def self.wall_time
    Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
  end
end

可以在 benchmark 代码中使用这些方法:

puts "****CPU Bound****"
c1, w1 = DiyProf::cpu_time, DiyProf::wall_time
10000.times do |i|
  Math.sqrt(i)
end
c2, w2 = DiyProf::cpu_time, DiyProf::wall_time
puts "CPU time\t=\t#{c2-c1}\nWall time\t=\t#{w2-w1}"

puts "\n****IO Bound****"
require 'tempfile'

c1, w1 = DiyProf::cpu_time, DiyProf::wall_time
1000.times do |i|
  Tempfile.create('file') do |f|
    f.puts(i)
  end
end
c2, w2 = DiyProf::cpu_time, DiyProf::wall_time
puts "CPU time\t=\t#{c2-c1}\nWall time\t=\t#{w2-w1}"

运行这些代码会得出类似以下的结果:

****CPU Bound****
CPU time    =   5038
Wall time   =   5142

****IO Bound****
CPU time    =   337898
Wall time   =   475864

这些清楚地展现了单个 CPU 内核的情况,在仅运行 CPU 密集型指令时 CPU time 和 Wall time 几乎相等,而运行 I/O 密集型指令时 CPU time 总是少于 Wall time.

概括

我们学习了 CPU time 和 Wall time 的含义与差异,以及什么时候用哪种。与此同时,写了一些 Ruby 代码来测量 CPU time 和 Wall time 来为我们做的 CPU 分析器测量时间。在第三部分我们将讨论 Ruby TracePoint API 并利用它做一个仪表分析器。

原文链接:http://crypt.codemancers.com/posts/2015-03-06-diy-ruby-cpu-profiling-part-i/

OneAPM for Ruby 能够深入到所有 Ruby 应用内部完成应用性能管理和监控,包括代码级别性能问题的可见性、性能瓶颈的快速识别与追溯、真实用户体验监控、服务器监控和端到端的应用性能管理。 想阅读更多技术文章,请访问 OneAPM 官方博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容