日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Ruby 和 Python 分析器是如何工作的?

發布時間:2025/3/15 python 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Ruby 和 Python 分析器是如何工作的? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


原文出處: Julia Evans???譯文出處:開源中國


你好! 我作為一名編寫Ruby?profiler的先驅,我想對現有的Ruby和Python?profiler如何工作進行一次調查。 這也有助于回答很多人的問題:“你怎么寫一個profiler?”


在這篇文章中,我們只關注CPUprofiler(而不是內存/堆profiler)。 我將解釋一些編寫profiler的一般基本方法,給出一些代碼示例,以及大量流行的Ruby和Pythonprofiler的例子,并告訴你它們是如何工作的。


在這篇文章中可能會有一些錯誤(為了研究這篇文章,我閱讀了14個不同的分析庫的代碼部分),請讓我們開始吧!


2種不同的profilers


有兩種基本CPU?profilers類型 –?sampling?profilers和tracing?profilers。


tracingprofilers記錄您的程序所調用的每個函數,然后在最后打印出報告。 samplingprofilers采用更加統計化的方法 – 他們每隔幾毫秒記錄程序的堆棧情況,然后報告結果。


使用sampling?profilers而不是tracing?profilers的主要原因是sampling?profilers的開銷較低。 如果每秒只抽取20或200個樣本,那不會花費多少時間。 而且它們非常有效率 – 如果您遇到嚴重的性能問題(比如80%的時間花費在1個慢速函數上),那么每秒200個樣本通常就足以確定那個函數的問題所在了!


分析器


下邊類出了我們這篇文章要討論的分析器(來源)。我之后將會解釋表格中的術語(setitimer,?rb_add_event_hook,?ptrace)。這里最有趣的是,所有的分析器都是通過一小部分函數的特性實現的。


python分析器


Name


Kind


How it works





cProfile

Tracing

PyEval_SetProfile

line_profiler

Tracing

PyEval_SetTrace

pyflame?(blog post)

Sampling

ptrace + custom timing

stacksampler

Sampling

setitimer

statprof

Sampling

setitimer

vmprof

Sampling

setitimer

pyinstrument

Sampling

PyEval_SetProfile

gprof?(greenlet)

Tracing

greenlet.settrace

python-flamegraph

Sampling

profiling thread + custom timing

gdb hacks

Sampling

ptrace


“gbd hacks”并不完全是一個Python分析器:它是一個講述如何實現用腳本包裝gdb來實現hacky分析器的鏈接。由于新版本的gdb事實上會展開Python堆棧,所以也是和Python有關的。一種簡化版的pyflame。


Ruby分析器


Name


Kind


How it works





stackprof?by tmm1

Sampling

setitimer

perftools.rb?by tmm1

Sampling

setitimer

rblineprof?by tmm1

Tracing

rb_add_event_hook

ruby-prof

Tracing

rb_add_event_hook

flamegraph

Sampling

stackprof gem


這些分析器中幾乎所有的都存在你的進程里面。


在我們開始詳細分析這些分析器之前,有一個非常重要的事情需要說明一下:除fyflame外所有的分析器都運行在你的Python/Ruby進程里面。如果你在一個Python/Ruby程序里面,你通常可以很容易的獲取該程序的堆棧。例如下邊代碼中的簡單的Python程序答應出每一個運行線程的堆棧:


1

2

3

4

5

6

7

8

9

10

11

12

import?sys

import?traceback

def?bar():

foo()

def?foo():

????for?_,?frame?in?sys._current_frames().items():

????????for?line?in?traceback.extract_stack(frame):

????????????print?line

bar()


你可以從下邊的輸出里面看到堆棧的函數名,行號,文件名等你在做分析的時候需要的所有信息。


1

2

3

(‘test2.py’,?12,?‘<module>’,?‘bar()’)

(‘test2.py’,?5,?‘bar’,?‘foo()’)

(‘test2.py’,?9,?‘foo’,?‘for?line?in?traceback.extract_stack(frame):’)


在Ruby程序中,獲取堆棧也很容易:你只需要通過caoller來獲取堆棧。


這些分析器處于性能考慮都是C擴展所有它們有一點不一樣,但是Ruby/Python程序的C擴展也可以很容易的獲取調用堆棧。


追蹤分析器是如何工作的


我調查過上邊表格中所有的追蹤分析器:rblineprof、ruby-prof和cProfile。它們工作原理基本相同。它們都記錄所有的函數調用并且用C語言編寫來降低耗時。


它們是如何工作的呢?Ruby和Python都允許指定一個回調函數,當各種解釋事件(例如調用一個函數或者執行一行代碼)發生的時候調用。當回調函數被調用的時候,會記錄堆棧供以后分析。


我認為確切了解在代碼中哪里設置這些回調函數是很有用的,所以我連接了所有在github上邊的相關代碼。


在Python中,可以通過PyEval_SetTrace或者?PyEval_SetProfile設置回調函數。在Python官方文檔的分析和追蹤里有說明。文檔中說道:除了追蹤函數會收到line-number事件外“PyEval_SetTrace和PyEval_SetProfile一樣。


代碼:


line_profiler 使用PyEval_SetTrace設置回調:看line_profiler.pyx的157行


cProfiles 使用PyEval_SetProfile設置回調:看_lsprof.c的693行(cProfile是用Isprof實現的)


在Ruby里,你可以用rb_add_event_hook來設置回調,我找不到任何關于此處是如何調用的文檔


1

2

3

4

rb_add_event_hook(prof_event_hook,

??????RUBY_EVENT_CALL?|?RUBY_EVENT_RETURN?|

??????RUBY_EVENT_C_CALL?|?RUBY_EVENT_C_RETURN?|

??????RUBY_EVENT_LINE,?self);


prof_event_hook的類型是


1

2

static?void

prof_event_hook(rb_event_flag_t?event,?VALUE?data,?VALUE?self,?ID?mid,?VALUE?klass)


這看起來像極了Python的PyEval_SetTrace,但是比Python更靈活——您可以選擇你關注的事件類型(就像“函數調用”一樣)。


代碼:


ruby-prof 調用rb_add_event在:ruby-prof.c line 329


rblineprof調用rb_add_event_hook在:rblineprof.c line 649

追蹤分析器的缺點


追蹤分析器的主要的缺點是它的實現方式是對于每個函數/行代碼都執行固定的次數,這樣可能使你做出錯誤的決定。例如,如果你有某個事物的兩個實現:一個通過大量的函數調用實現,另一個沒有大量函數調用,兩個實現耗時相同,有大量函數調用的相比沒有大量函數調用的在分析的時候會變得慢。


為了測試這一點,我做了一個包含下邊內容的小文件test.py,并且比較了python -mcProfile test.py和python test.py的耗時。python test.py執行需要大約0.6秒,python -mcProfile test.py執行需要大約1秒。對于這個特定的例子cProfile引入了額外的大約60%的開銷。


1

2

3

4

5

6

7

def?recur(n):

????if?n?==?0:

return

recur(n–1)

for?i?in?range(5000):

recur(700)


cProfile文檔中說:


Python的解釋語言的特性往往會增加執行的開銷,對于典型的應用確定性分析僅僅會增加很少運行開銷。


這似乎是一個合理的說法:上邊的示例(執行350萬次函數調用)顯然不是個典型的Python程序,并且幾乎任何其他程序開銷都比該示例小。

我沒有測試ruby-prof(一個ruby追蹤分析器)的開銷,但是它的README說:


大多數程序開分析器耗時將會是原來的兩倍,并且高度遞歸程序(斐波那契數列)耗時將會是原來的三倍。


采樣分析器都怎么工作的:setitimer


現在討論第二種分析器:采樣分析器。


大多數Ruby和Python的采樣分析器都是通過系統調用setitimer實現的。這是怎么回事呢?


好吧,比方說你想要每秒獲取一個程序的堆棧50次,一種方法是:


請求Linux內核每20毫秒給你發送一個信號(使用系統調用setitimer)

注冊一個信號處理器在每次獲得信號的時候記錄堆棧。


當結束分析的時候,請求Linux停止發送信號并且打印輸出。


如果你想要看一個實際的用setitimer實現采樣分析器的例子的話,我認為stacksampler.py是一個最好的例子,stacksampler.py是一個有用的有效的分析器并且代碼只有大約100行,好酷啊!


stacksampler.py只有100多行的一個原因是:當你把一個Python函數注冊成信號處理器的時候,該函數被傳送到你的Python程序的當前堆棧中。所以stacksampler.py信號處理器注冊是非常簡單的:


1

2

3

4

5

6

7

8

def?_sample(self,?signum,?frame):

???stack?=?[]

???while?frame?is?not?None:

stack.append(self._format_frame(frame))

???????frame?=?frame.f_back

???stack?=?‘;’.join(reversed(stack))

???self._stack_counts[stack]?+=?1


它只是將堆棧從堆棧幀中取出來并且增加堆棧查看計數,非常簡單!非常酷!


我們看繼續剩下的使用setitimer的分析器并找到它們調用settimer的代碼:


stackprof?(Ruby): in?stackprof.c line 118

perftools.rb?(Ruby): in?this patch which seems to be applied when the gem is compiled (?)

stacksampler?(Python):?stacksampler.py line 51

statprof?(Python):?statprof.py line 239

vmprof?(Python):?vmprof_unix.c line 294


關于setitimer很重要的一點是,你需要決定如何計算時間。你想要真正的20 ms的“掛鐘”時間?你想要20 ms的用戶CPU時間?或者20 ms的用戶+系統CPU時間?如果你仔細看電話網站上的內容,你就會發現,這些分析器實際上對setitimer做出了不同的選擇 — 有時候它是可配置的,有時候卻不可。setitimer手冊頁十分精悍,并且值得去讀懂上面所有的觀點。


@mgedmin?在推特上指出了一個使用setitimer時出現的有趣的問題,這個問題和這個問題擁有的一系列更多細節。


一個有趣的基于setitimer分析器的問題就是定時器產生的信號!信號有時候能中斷系統調用!系統調用有時候需要幾毫秒!如果測試太平凡,你會讓你的程序永遠循環執行系統調用!


不使用setitimer的采樣分析器


有些采樣分析器不使用setitimer:


pyinstrument使用PyEval_SetProfile(所以它在某種程度上是跟蹤分析器),但是當它的跟蹤回調函數被調用時,它并不總是收集堆棧樣本。下面是選擇何時測試堆棧跟蹤的代碼。更多信息,請看這篇博客文章。?(真相:?setitimer帶你了解Python中的主線程)


pyflame簡要介紹了Python代碼在外部調用ptracesystem的過程。根本上來講,它只是一個抓取樣本,睡眠,重復的循環,這里是sleep調用。


python-flamegraph以類似的方式在你的Python操作中開啟一個新的線程并且抓取堆棧跟蹤,睡眠,和重復。這里是sleep調用。


所有這3個分析器使用掛鐘定時采樣。


pyflame 博客


有很多關于pyflame是如何工作的。我不打算在這里進行介紹,但是Evan Klitke寫了很多關于它的非常好的博客:


Pyflame:超級工程的Ptracing的Python分析器來介紹pyflame


Pyflame雙解析器模式關于如何同時支持Python2和Python3


意想不到的python ABI變動增加了Python3.6的支持


釋放多線程Python堆棧


Pyflame打包


在Python中一個關于ptrace+syscalls的有趣的問題


使用ptrace的樂趣和好處,ptrace(續)


還有很多在?https://eklitzke.org/。所有有趣的東西,我會更詳細地閱讀——也許ptrace是比實現一個Ruby分析器process_vm_readv更好的方法!(process_vm_readv開銷低,因為它不會阻斷進程,但它也可以給你一個不一致的快照,因為它不會阻斷進程:))


先講解到這里了!


在這篇文章中我沒有涉及很多重要的細節 – 比如我基本上說vmprof和stacksampler是一樣的(但實際上它們不是 – vmprof支持線性分析和用C語言編寫的Python函數分析,我相信這在分析器中引入了更多的復雜性)。 但一些基本原理是一樣的,所以我認為這項調查是一個很好的起點。


來源:數盟


精彩活動

福利 · 閱讀 | 免費申請讀大數據新書 第23期

推薦閱讀

2017年數據可視化的七大趨勢!?

全球100款大數據工具匯總(前50款)?

論大數據的十大局限

大數據時代的10個重大變革

大數據七大趨勢 第一個趨勢是物聯網


Q:?你在Python學習使用中都有哪些心得?

歡迎留言與大家分享

請把這篇文章分享給你的朋友

轉載 / 投稿請聯系:hzzy@hzbook.com

更多精彩文章,請在公眾號后臺點擊“歷史文章”查看

總結

以上是生活随笔為你收集整理的Ruby 和 Python 分析器是如何工作的?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。