å ã®èšäºã¯ããã§èªãããšãã§ããŸãã
ããã«ã¡ã¯ïŒRubyãããã¡ã€ã©ãŒã®ä»å±åãšããŠãæ¢åã®Rubyããã³Pythonãããã¡ã€ã©ãŒãã©ã®ããã«æ©èœãããã«ã€ããŠè©±ããããšæããŸããããŸããå€ãã®äººãç§ã«å°ããããããã¡ã€ã©ãŒã®æžãæ¹ããšãã質åã«çããã®ã«ã圹ç«ã¡ãŸãã
ãã®èšäºã§ã¯ãããã»ããµãããã¡ã€ã©ãŒã«çŠç¹ãåœãŠãŸãïŒããšãã°ãã¡ã¢ãª/ããŒããããã¡ã€ã©ãŒã§ã¯ãããŸããïŒããããã¡ã€ã©ãŒãäœæããããã®ããã€ãã®åºæ¬çãªã¢ãããŒããã«ããŒããããã€ãã®ã³ãŒãäŸãæäŸããRubyãšPythonã§äººæ°ã®ãããããã¡ã€ã©ãŒã®å€ãã®äŸã瀺ããããããå éšã§ã©ã®ããã«æ©èœãããã瀺ããŸãã
ããããèšäºã«èª€ãããããããããŸããïŒãããæžãæºåãšããŠãç§ã¯ãããã¡ã€ãªã³ã°ã®ããã«14ã®ã©ã€ãã©ãªã®ã³ãŒããéšåçã«èª¿ã¹ãŸããããããŠç§ã¯ä»ãŸã§ãããã®å€ãã«ç²ŸéããŠããŸããã§ããïŒãããã§ããããèŠã€ãããç§ã«ç¥ãããŠãã ãã..ã
2çš®é¡ã®ãããã¡ã€ã©ãŒ
ããã»ããµãããã¡ã€ã©ã«ã¯ããµã³ããªã³ã°ãããã¡ã€ã©ãšãã¬ãŒã¹ãããã¡ã€ã©ã®2ã€ã®äž»èŠãªã¿ã€ãããããŸãã
ãã¬ãŒã¹ãããã¡ã€ã©ãŒã¯ãããã°ã©ã å ã®ãã¹ãŠã®é¢æ°åŒã³åºããèšé²ããæçµçã«ã¬ããŒããæäŸããŸãããµã³ããªã³ã°ãããã¡ã€ã©ãŒã¯çµ±èšçã¢ãããŒããåããæ°ããªç§ããšã«ã¹ã¿ãã¯ãæžã蟌ã¿ããã®ããŒã¿ã«åºã¥ããŠã¬ããŒããçæããŸãã
ãã¬ãŒã¹ãããã¡ã€ã©ãŒã®ä»£ããã«ãµã³ããªã³ã°ãããã¡ã€ã©ãŒã䜿çšããäž»ãªçç±ã¯ã軜éã§ããããã§ããããªãã¯æ¯ç§20ãŸãã¯200æã®åçãæ®ããŸã-ããã¯ããã»ã©æéã¯ããããŸããããã®ãããªãããã¡ã€ã©ãŒã¯ãæ·±å»ãªããã©ãŒãã³ã¹ã®åé¡ãããå ŽåïŒæéã®80ïŒ ã1ã€ã®é ã颿°ã®åŒã³åºãã«è²»ããããŠããïŒã«éåžžã«å¹æçã§ããåé¡ã®é¢æ°ãç¹å®ããã«ã¯ã1ç§ããã200ã®ã¹ãããã·ã§ããã§ååã ããã§ãã
ãããã¡ã€ã©ãŒ
次ã«ããã®èšäºã§èª¬æãããããã¡ã€ã©ãŒã®æŠèŠã説æããŸãïŒããããïŒããã®èšäºã§äœ¿çšãããŠããçšèªïŒsetitimerãrb_add_event_hookãptraceïŒã«ã€ããŠã¯åŸã§èª¬æããŸããè峿·±ãããšã«ããã¹ãŠã®ãããã¡ã€ã©ãŒã¯ãåºæ¬çãªæ©èœã®å°ããªã»ããã䜿çšããŠå®è£ ãããŠããŸãã
Pythonãããã¡ã€ã©ãŒ
ãGdbããã¯ãã¯å®éã«ã¯Pythonãããã¡ã€ã©ãŒã§ã¯ãããŸãããããã«ãŒãããã¡ã€ã©ãŒãgdbã®ã·ã§ã«ã¹ã¯ãªããã©ãããŒãšããŠå®è£ ããæ¹æ³ã説æããWebãµã€ãã«ãªã³ã¯ããŠããŸããæ°ããããŒãžã§ã³ã®gbdã¯å®éã«Pythonã¹ã¿ãã¯ããããã€ãããããããã¯ç¹ã«Pythonã«é¢ãããã®ã§ããè²§ãã人ã ã®ããã®pyflameã®ãããªãã®ã
Rubyãããã¡ã€ã©ãŒ
ãããã®ãããã¡ã€ã©ãŒã®ã»ãšãã©ãã¹ãŠãããã»ã¹å ã«ååšããŸããããã®ãããã¡ã€ã©ãŒ
ã®è©³çްã«å ¥ãåã«ãéåžžã«éèŠãªããšã1ã€ãããŸããpyflameãé€ããããã®ãããã¡ã€ã©ãŒã¯ãã¹ãŠãPython / Rubyããã»ã¹å ã§å®è¡ãããŸããPython / Rubyããã°ã©ã å ã«ããå Žåã¯ãéåžžãã¹ã¿ãã¯ã«ç°¡åã«ã¢ã¯ã»ã¹ã§ããŸããããšãã°ãå®è¡äžã®åã¹ã¬ããã®ã¹ã¿ãã¯ã®å 容ãåºåããåçŽãªPythonããã°ã©ã ãæ¬¡ã«ç€ºããŸãã
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()
ãããã³ã³ãœãŒã«åºåã§ããã¹ã¿ãã¯ããã®é¢æ°åãè¡çªå·ããã¡ã€ã«åãªã©ããããã¡ã€ãªã³ã°ããå Žåã«å¿ èŠãªãã®ããã¹ãŠå«ãŸããŠããããšãããããŸãã
('test2.py', 12, '<module>', 'bar()')
('test2.py', 5, 'bar', 'foo()')
('test2.py', 9, 'foo', 'for line in traceback.extract_stack(frame):')
ããã¯Rubyã§ããç°¡åã§ãïŒããªãã䜿çšããããšãã§ããçºä¿¡è ããããã¹ã¿ãã¯ãååŸããŸãã
ãããã®ãããã¡ã€ã©ãŒã®ã»ãšãã©ã¯Cã®ããã©ãŒãã³ã¹æ¡åŒµæ©èœã§ããããããããã«ç°ãªããŸãããRuby / Pythonããã°ã©ã çšã®ãã®ãããªæ¡åŒµæ©èœã¯ãåŒã³åºãã¹ã¿ãã¯ã«ãç°¡åã«ã¢ã¯ã»ã¹ã§ããŸãã
ãã¬ãŒã¹ãããã¡ã€ã©ãŒã®ããã¿
äžèšã®è¡šã«ã ãã¹ãŠã®Rubyããã³Pythonãã¬ãŒã¹ãããã¡ã€ã«ããªã¹ãããŸããïŒrblineprofãruby-profãline_profilerãããã³cProfileããããã¯ãã¹ãŠåãããã«æ©èœããŸãããããã¯ãã¹ãŠã®é¢æ°åŒã³åºããèšé²ãããªãŒããŒããããåæžããããã®Cæ¡åŒµæ©èœã§ãã
ãããã¯ã©ã®ããã«æ©èœããŸããïŒ RubyãšPythonã®äž¡æ¹ã§ãããŸããŸãªã€ã³ã¿ãŒããªã¿ãŒã€ãã³ãïŒã颿°åŒã³åºããããã³ãŒãå®è¡è¡ããªã©ïŒãçºçãããšãã«ããªã¬ãŒãããã³ãŒã«ããã¯ãæå®ã§ããŸããã³ãŒã«ããã¯ãåŒã³åºããããšãåŸã§åæããããã«ã¹ã¿ãã¯ãæžã蟌ãŸããŸãã
ãããã®ã³ãŒã«ããã¯ãã³ãŒãå ã®ã©ãã«ããããæ£ç¢ºã«ç¢ºèªãããšåœ¹ç«ã€ã®ã§ãgithubã®é¢é£ããã³ãŒãè¡ã«ãªã³ã¯ããŸãã
Pythonã§ã¯ã
PyEval_SetTraceãŸãã¯ã䜿çšããŠã³ãŒã«ããã¯ãã«ã¹ã¿ãã€ãºã§ããŸãPyEval_SetProfileãããã¯ããã¥ã¡ã³ãã»ã¯ã·ã§ã³ã§èª¬æãããŠããŸãPythonã§ã®ãããã¡ã€ãªã³ã°ãšãã¬ãŒã¹ãããã¬ãŒã¹æ©èœãè¡çªå·ã€ãã³ããåä¿¡ããããšPyEval_SetTraceãPyEval_SetProfileé€ããŠãåæ§ã§ãããšè¡šç€ºãããŸãã
ã³ãŒãïŒ
line_profilerã䜿çšããŠã³ãŒã«ããã¯ãèšå®ããŸãPyEval_SetTraceïŒ157line_profiler.pyxè¡ç®ãåç §cProfileã䜿çšããŠã³ãŒã«ããã¯ãèšå®ããŸãPyEval_SetProfileïŒ_lsprof.c693è¡ç®ãåç §ïŒcProfileã¯lsprofã䜿çšããŠå®è£ ãããŸãïŒ
Rubyã§ã¯ãã䜿çšããŠã³ãŒã«ããã¯ãã«ã¹ã¿ãã€ãºã§ããŸã
rb_add_event_hookãããã«é¢ããããã¥ã¡ã³ãã¯èŠã€ãããŸããã§ãããããã®ããã«èŠããŸãã
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ïŒ
static void
prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
PyEval_SetTracePythonãš
䌌ãŠããŸãããããæè»ãªåœ¢åŒã§ããéç¥ããã€ãã³ããéžæã§ããŸãïŒããšãã°ãã颿°åŒã³åºãã®ã¿ãïŒã
ã³ãŒãïŒ
tracing-
ãã®æ¹æ³ã§å®è£ ããããã¬ãŒã¹ãããã¡ã€ã©ãŒã®äž»ãªæ¬ ç¹ã¯ãå®è¡ããã颿°/è¡åŒã³åºãããšã«äžå®éã®ã³ãŒãã远å ãããããšã§ããããã¯ããªãã«ééã£ã決å®ããããå¯èœæ§ããããŸãïŒããšãã°ãäœãã®å®è£ ã2ã€ããå ŽåïŒ1ã€ã¯é¢æ°åŒã³åºããå€ããã®ããã1ã€ã¯ãªããã®ã§ãéåžžã¯åãæéãããããŸãïŒããããã¡ã€ãªã³ã°æã«ã颿°åŒã³åºããå€ãæåã®å®è£ ã¯é ã衚瀺ãããŸãã
説æã®ããã«
test.pyãæ¬¡ã®å
容ã§ååãä»ããå°ããªãã¡ã€ã«ãäœæããå®è¡æépython -mcProfile test.pyãšãæ¯èŒããŸããpython test.pyãpython. test.pyçŽ0.6ç§ãšpython -mcProfile test.pyçŽ1ç§ã§å®äºããŸããããããã£ãŠããã®ç¹å®ã®äŸã§ã¯cProfileãçŽ60ïŒ
ã®ãªãŒããŒãããã远å ããŸããã
ããã¥ã¡ã³ãã«cProfileãããšã
Pythonã®è§£éãããæ§è³ªã«ãããå®è¡æã®ãªãŒããŒããããéåžžã«å€§ãããªããããæ±ºå®è«çãããã¡ã€ãªã³ã°ã§ã¯ãéåžžã®ã¢ããªã±ãŒã·ã§ã³ã§åŠçã®ãªãŒããŒããããå°ãå¢ããåŸåããããŸãã
ããã¯ããªãåççãªã¹ããŒãã¡ã³ãã®ããã§ããåã®äŸïŒ350äžåã®é¢æ°åŒã³åºãã®ã¿ãè¡ãïŒã¯æããã«éåžžã®Pythonããã°ã©ã ã§ã¯ãªããä»ã®ã»ãšãã©ã®ããã°ã©ã ã®ãªãŒããŒãããã¯å°ãªããªããŸããRubyãã¬ãŒã¹ãããã¡ã€ã©ãŒã
ãã§ãã¯ããŠããŸãã
ruby-profãããã®READMEã«ã¯æ¬¡ã®ããã«æžãããŠããŸãã
ã»ãšãã©ã®ããã°ã©ã ã®å®è¡é床ã¯çŽååã§ãããååž°æ§ã®é«ãããã°ã©ã ïŒFibonacciã·ãªãŒãºãã¹ããªã©ïŒã®å®è¡é床ã¯3åé ããªããŸãã
ãµã³ããªã³ã°ãããã¡ã€ã©ãŒã®äžè¬çãªä»çµã¿ïŒsetitimer
2çªç®ã®çš®é¡ã®ãããã¡ã€ã©ãŒã«ã€ããŠè©±ãæéã§ãïŒãããã¡ã€ã©ãŒã®ãµã³ããªã³ã°ïŒ
Rubyããã³Pythonã®ã»ãšãã©ã®ãµã³ããªã³ã°ãããã¡ã€ã©ãŒã¯ãã·ã¹ãã åŒã³åºãã䜿çšããŠå®è£ ãããŸã
setitimerãããã¯äœã§ããïŒ
ããã°ã©ã ã¹ã¿ãã¯ã®ã¹ãããã·ã§ããã1ç§éã«50åååŸãããšããŸããããã¯æ¬¡ã®ããã«å®è¡ã§ããŸãã
- Linuxã«ãŒãã«ã«20ããªç§ããšã«ä¿¡å·ãéä¿¡ããããã«äŸé ŒããŸãïŒã·ã¹ãã åŒã³åºãã䜿çš
setitimerïŒã - ä¿¡å·ãåä¿¡ãããšãã«ãã¹ã¿ãã¯ã¹ãããã·ã§ããã®ä¿¡å·ãã³ãã©ãŒãç»é²ããŸãã
- ãããã¡ã€ãªã³ã°ãå®äºããããLinuxã«ã·ã°ããªã³ã°ã忢ããŠçµæãæäŸããããã«äŸé ŒããŠãã ããã
setitimerãµã³ããªã³ã°ãããã¡ã€ã©ãŒãå®è£
ããããã®
å®éã®äœ¿çšäŸãèŠããå Žåãstacksampler.pyæè¯ã®äŸã¯äŸ¿å©ã§æ©èœãããããã¡ã€ã©ãŒã§ãããPythonã§ã¯çŽ100è¡ã§ãããšæããŸãããããŠãããã¯ãšãŠãã¯ãŒã«ã§ãïŒPythonã§100è¡ããããããªã
çç±
stacksampler.pyã¯ãPython颿°ãã·ã°ãã«ãã³ãã©ãŒãšããŠç»é²ãããšããã®é¢æ°ãããã°ã©ã ã®çŸåšã®ã¹ã¿ãã¯ã«æž¡ãããããã§ãããããã£ãŠãã·ã°ãã«ãã³ãã©ãŒã®stacksampler.pyç»é²ã¯éåžžã«ç°¡åã§ãã
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èŠãŠãã³ãŒãã®ã©ãã§åŒã³åºããŠãããã調ã¹ãŠã¿ãŸãããsetitimerã
stackprof(Ruby):stackprof.c118perftools.rb(Ruby): , , , , gem (?)stacksampler(Python):stacksampler.py51statprof(Python):statprof.py239vmprof(Python):vmprof_unix.c294
ã«ã€ããŠã®éèŠãªããš
setitimer-ããªãã¯æéãæ°ããæ¹æ³ã決ããå¿
èŠããããŸãã 20msã®ãªã¢ã«ã¿ã€ã ãå¿
èŠã§ããïŒ 20msã®ãŠãŒã¶ãŒcpuæéïŒ 20msãŠãŒã¶ãŒ+ã·ã¹ãã cpuæéïŒäžèšã®ãªã³ã¯ãããèŠããšããããã®ãããã¡ã€ã©ãŒãå®éã«ã¯ç°ãªããã®ã䜿çšããŠããããšsetitimerãããããŸããåäœãã«ã¹ã¿ãã€ãºå¯èœãªå Žåãšããã§ãªãå ŽåããããŸããããã¥ã¢ã«ããŒãžã¯setitimerçããèãããããã¹ãŠã®æ§æã«ã€ããŠèªã䟡å€ããããŸããTwitterã§1ã€ã®è峿·±ããŠãŒã¹ã±ãŒã¹
@mgedminãææããŸããsetitimerããã®åé¡ãšãã®åé¡ã¯è©³çްãæããã«ããŸãã
ã«åºã¥ããããã¡ã€ã©ãŒã®1ã€ã®è峿·±ãæ¬ ç¹
setitimer-ã©ã®ã¿ã€ããŒãä¿¡å·ãããªã¬ãŒããŸããïŒä¿¡å·ãã·ã¹ãã åŒã³åºããäžæããããšããããŸãïŒã·ã¹ãã åŒã³åºãã«ã¯æ°ããªç§ãããå ŽåããããŸããã¹ãããã·ã§ãããé »ç¹ã«äœæããå Žåã¯ãããã°ã©ã ã«ã·ã¹ãã åŒã³åºããç¡æéã«å®è¡ãããããšãã§ããŸãã
setitimerã䜿çšããªããµã³ããªã³ã°ãããã¡ã€ã©ãŒ
ã䜿çšããªããµã³ããªã³ã°ãããã¡ã€ã©ãŒãããã€ããããŸã
setitimerã
pyinstrumentã䜿çšããŸãPyEval_SetProfileïŒã€ãŸããäžçš®ã®ãã¬ãŒã¹ãããã¡ã€ã©ãŒã§ãïŒãããã¬ãŒã¹ã³ãŒã«ããã¯ãåŒã³åºããããšãã«ã¹ã¿ãã¯ã¹ãããã·ã§ãããåžžã«åéãããšã¯éããŸãããã¹ã¿ãã¯ãã¬ãŒã¹ã¹ãããã·ã§ããã®ã¿ã€ãã³ã°ãéžæããã³ãŒãã¯æ¬¡ã®ãšããã§ãããã®ããã°ã§ãã®ãœãªã¥ãŒã·ã§ã³ã®è©³çްãèªãã§ãã ãããïŒåºæ¬çã«ïŒsetitimerPythonã§ã¡ã€ã³ã¹ã¬ããã®ã¿ããããã¡ã€ãªã³ã°ã§ããŸãïŒpyflameã·ã¹ãã åŒã³åºãã䜿çšããŠãããã»ã¹ã®å€éšã§Pythonã³ãŒãããããã¡ã€ã«ããŸãptraceã圌ã¯ã«ãŒãã䜿çšããŠåçãæ®ããäžå®æéç ããåãããšãç¹°ãè¿ããŸãããããåŸ ã€ããã®åŒã³ããã§ããpython-flamegraphåæ§ã®ã¢ãããŒããåããPythonããã»ã¹ã§æ°ããã¹ã¬ãããéå§ããã¹ã¿ãã¯ãã¬ãŒã¹ãååŸããã¹ãªãŒãããŠãæåããããçŽããŸãããããåŸ æ©ã®åŒã³åºãã§ãã
ããã3ã€ã®ãããã¡ã€ã©ãŒã¯ãã¹ãŠããªã¢ã«ã¿ã€ã ã®ã¹ãããã·ã§ãããååŸããŸãã
Pyflameã®ããã°æçš¿
ãã®æçš¿ã§ã¯ãã»ãšãã©ãã¹ãŠã®æéã以å€ã®ãããã¡ã€ã©ãŒã«è²»ãããŸãã
pyflameããPythonããã°ã©ã ãå¥ã®ããã»ã¹ãããããã¡ã€ãªã³ã°ãããããå®éã«æãèå³ãæã£ãŠããŸãããã®ãããRubyãããã¡ã€ã©ãŒãåæ§ã«æ©èœãããããšèããŠããŸãã
ãã¡ããããã¹ãŠãç§ã説æãããããå°ãè€éã§ãã詳现ã«ã€ããŠã¯èª¬æããŸããããEvanKlitzkeã¯åœŒã®ããã°ã«ããã«é¢ããå€ãã®åªããèšäºãæžããŠããŸãã
- PyflameïŒUberEngineeringã®PythonçšPtracingProfiler -pyflameã®æŠèŠ
- Pyflameãã¥ã¢ã«ã€ã³ã¿ãŒããªã¿ãŒã¢ãŒã-Python2ãšPython3ã®äž¡æ¹ãåæã«ãµããŒãããæ¹æ³
- äºæããªãPythonABIã®å€æŽ-Python3.6ã®ãµããŒãã®è¿œå ã«ã€ããŠã
- ãã«ãã¹ã¬ããPythonã¹ã¿ãã¯ã®ãã³ã;
- Pyflameããã±ãŒãž;
ptracePythonã§ã®ã·ã¹ãã åŒã³åºãã- 楜ãããšå©çã®ããã«ptraceã䜿çšãããptraceïŒç¶ãïŒ ;
詳现ã«ã€ããŠã¯ãeklitzke.orgãã芧ãã ããããããã¯ãã¹ãŠãç§ããããã詳ããèªãéåžžã«è峿·±ããã®ã§ãããããããRubyãããã¡ã€ã©ãŒãå®è£ ãã
ptraceãããåªããŠããããšãprocess_vm_readvããããŸããprocess_vm_readvããã»ã¹ã忢ããªããããªãŒããŒããããå°ãªããªããŸãããããã»ã¹ã忢ããªãããã誀ã£ãã¹ãããã·ã§ããã衚瀺ãããå¯èœæ§ããããŸã:)ãç§ã®å®éšã§ã¯ãççŸããç»åãååŸããããšã¯å€§ããªåé¡ã§ã¯ãããŸããã§ããããããã§äžé£ã®å®éšãè¡ããšæããŸãã
ããã仿¥ã®ãã¹ãŠã§ãïŒ
ãã®æçš¿ã§ã¯è§Šããªãã£ãéèŠãªåŸ®åŠãªç¹ããããããããŸããããšãã°ãåºæ¬çã«ãšã¯äŒŒãŠãã
vmprofãšèšããŸããstacksamplerïŒããã§ã¯ãããŸããïŒãvmprofæååãããã¡ã€ãªã³ã°ãšCã§èšè¿°ãããPython颿°ã®ãããã¡ã€ãªã³ã°ããµããŒãããŠããããããããã¡ã€ã©ãŒãããè€éã«ãªããšæããŸãïŒããããããããã¯åãåºæ¬ååã®ããã€ããæã£ãŠããã®ã§ã仿¥ã®ã¬ãã¥ãŒã¯è¯ãåºçºç¹ã«ãªããšæããŸãã
pytestã®æç¡ã«ãããããTDDãç¡æã®ãŠã§ãããŒ