æè¡é¨éçºåºç¤ã°ã«ã¼ãã®ã·ã (@shia)ã§ãã æè¿ã¯ cookpad ã®ã¡ã¤ã³ã¬ãã¸ããªãéçºããããç°å¢ã«æ¹åããããã«æ§ã ãªè©¦ã¿ããã¦ãã¾ãã ãã®è¨äºã§ã¯ãã®è©¦ã¿ã®ä¸ã¤ã¨ãã¦ä¸è¦ãª gem ãæ¤åºããåé¤ããæ¹æ³ãç´¹ä»ãããã¨æãã¾ãã
èæ¯
cookpad ã¯10年以ä¸ã«ããã£ã¦éç¨ããã¦ãã巨大ãªã¦ã§ãã¢ããªã±ã¼ã·ã§ã³ã§ãã 巨大ãã¤å¤ãã¢ããªã±ã¼ã·ã§ã³ã«ã¯æã¯ä½¿ã£ã¦ããããç¾å¨ã¯ä½¿ããã¦ãªãä¾åæ§ãªã©ãæè¡è² åµã¨ãã¦æºã¾ã£ã¦ãã¾ãã äºæ¥ç観ç¹ããæè¡çè² åµãå®å ¨è¿å´ããã®ã¯ã³ã¹ãã¨ã®ãã©ã³ã¹ãæªããã¨ãå¤ãã§ãã ããã¯20ä¸è¡ãè¶ ããããã¸ã§ã¯ããå¹¾ã¤ãæ±ãã¦ãã cookpad ã®ã¡ã¤ã³ã¬ãã¸ããªãä¾å¤ã§ã¯ãªãããã®è¦æ¨¡ãã使ã£ã¦ãªãã¨æãããä¾åæ§ãæ¢ãã ãã®ã大å¤ãªç¶æ³ã§ããã
ã©ãããã
人ãé å¼µãããæ©æ¢°ã«é å¼µãããã»ãã楽ãã§ããããä½ãã確å®ã§ãã ã§ãã®ã§ä»åã¯æªä½¿ç¨ã® gem ãæ¢ãããã« Ruby ã®é 延ãã¼ãä»çµã¿ã«ä¹ãã¾ããã é 延ãã¼ãã®ããã«ç¨æãããä»çµã¿ã«ããããå½ã¦ã使ç¨ããã¦ãã gem ã®ãªã¹ããåºãã¾ãã ãããå©ç¨ãã¦ä¾åãã¦ã gem ã®ãªã¹ãããæªä½¿ç¨ã§ãã gem ã®ãªã¹ããéå¼ããã¾ãã
InstructionSequence(iseq) ã¨ã¯
InstructionSequence(iseq) ã¨ã¯ Ruby ã®ã½ã¼ã¹ã³ã¼ããã³ã³ãã¤ã«ãã¦å¾ãããå½ä»¤ã®éåãæãã¾ãã ãã®å½ä»¤ã¯ Ruby VM ãç解ã§ãããã®ã§ãå iseq ã¯ããªã¼æ§é ã§æãç«ã¡ã¾ããä¾ãã°
class Cat def sleep end end
ãã®ã³ã¼ãããã¯Cat
ã¯ã©ã¹ã表ç¾ãã iseq ãä¸ã¤ã sleep
ã¡ã½ããã表ç¾ãã iseq ãä¸ã¤ä½ããã¾ã*1ãã
æ§é çã«ã¯ Cat
ã® iseq ã« sleep
ã® iseq ãå«ã¾ãã¦ããç¶æ
ã§ãã
ãããã詳ãã説æãè¦ããæ¹ã¯ RubyVM::InstructionSequence
ã®èª¬æããRubyã®ããã¿ãã¨ããæ¬ãããããã§ãã
ãããã¯å¼ç¤¾ã§ Ruby ã®å
é¨ãåãã Ruby Hack Challenge ã¨ããã¤ãã³ããä¸å®æã«éå¬ãããã®ã§åå ãã¦ã¿ãã®ãè¯ããæãã¾ãã åèè¨äº
InstructionSequence lazy loading
Ruby 2.3 ã§ã¯ iseq ã lazy loading ããã¨ããä»çµã¿ã試é¨çã«å°å ¥ããã¾ããã ãã®æ©è½ã¯ iseq ãåãã¦å®è¡ããæã¾ã§ä¸èº«ã®èªã¿è¾¼ã¿ãé 延ããããã¨ã§ã
- ã¢ããªã±ã¼ã·ã§ã³ã®ãã¼ãã£ã³ã°ãæ©ããªã
- ã¡ã¢ãªã¼ã®ä½¿ç¨éãæ¸ãã
ã¨ãããã¨ãçã£ã¦ãã¾ããã§ãããä»åã¯ãåãã¦å®è¡ããæã¾ã§ä¸èº«ã®èªã¿è¾¼ã¿ãé 延ããããããã«ç¨æãããä»çµã¿ã«èå³ãããã¾ãã iseq ã®å®ç¾©ãã¹ã first line number 㯠iseq ããç°¡åã«åãåºããã®ã§ãããããå©ç¨ããã°å®éã«ä½¿ç¨ããã gem ã®ãªã¹ããä½ãã¾ãã
ã©ãããããããå½ã¦ãã®ããè¦ãåã«å°ãã ã Ruby ã®ã³ã¼ããè¦ã¦ã¿ã¾ãããã
// https://github.com/ruby/ruby/blob/v2_4_3/vm_core.h#L415-L424 static inline const rb_iseq_t * rb_iseq_check(const rb_iseq_t *iseq) { #if USE_LAZY_LOAD if (iseq->body == NULL) { rb_iseq_complete((rb_iseq_t *)iseq); } #endif return iseq; }
rb_iseq_check
㯠iseq ãå®è¡ãããåã«å¼ã°ããé¢æ°ã§ãã
ãã㧠iseq ã®ä¸èº«ã空ãªã(ã¾ã å®è¡ããããã¨ããªã)ãä¸èº«ããã¼ããã¦ãã®ããããã¾ãã
å
ç¨è©±ããããã«ããã¯å®é¨çãªæ©è½ã§ãããã USE_LAZY_LOAD ããã¯ãã§å®£è¨ããã¦ãªãã¨ä½¿ããã¾ããã
ã§ãã®ã§æ®æ®µã¯ãªã«ãããå¼æ°ã¨ãã¦æ¸¡ããã iseq ãè¿ãã ãã®é¢æ°ã§ãã
ãã㧠iseq ã®ååå®è¡ã®ã¿ç¹å®ã®é¢æ°ãå¼ã³ãããã§å¿
è¦ãªãã®ã³ã°ä½æ¥ããã°è¯ãããã§ãã
ããã
ä¸è¨ã®ã³ã¼ãããã©ãããæãã®ããããæ¸ãã°ããã®ãç解ã§ããã¨æãã®ã§å®éã®ããããè¦ã¦ã¿ã¾ãããã 以ä¸ã®ããã㯠2.4.3 ãã¿ã¼ã²ããã¨ãã¦æ¸ããã¦ãã®ã§æ³¨æãã¦ãã ããã
--- iseq.c | 16 ++++++++++++++++ vm_core.h | 15 +++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/iseq.c b/iseq.c index 07d8828e9b..322dfb07dd 100644 --- a/iseq.c +++ b/iseq.c @@ -2482,3 +2482,19 @@ Init_ISeq(void) rb_undef_method(CLASS_OF(rb_cISeq), "translate"); rb_undef_method(CLASS_OF(rb_cISeq), "load_iseq"); } + +#if USE_EXECUTED_CHECK +void +rb_iseq_executed_check_dump(rb_iseq_t *iseq) +{ + iseq->flags |= ISEQ_FL_EXECUTED; + char *output_path = getenv("IE_OUTPUT_PATH"); + if (output_path == NULL) { return; } + + char *iseq_path = RSTRING_PTR(rb_iseq_path(iseq)); + FILE *fp = fopen(output_path, "a"); + fprintf(fp, "%s:%d\n", iseq_path, FIX2INT(rb_iseq_first_lineno(iseq))); + fclose(fp); +} +#endif diff --git a/vm_core.h b/vm_core.h index 8e2b93d8e9..96f14445f9 100644 --- a/vm_core.h +++ b/vm_core.h @@ -412,6 +412,16 @@ struct rb_iseq_struct { const rb_iseq_t *rb_iseq_complete(const rb_iseq_t *iseq); #endif +#ifndef USE_EXECUTED_CHECK +#define USE_EXECUTED_CHECK 1 +#endif + +#define ISEQ_FL_EXECUTED IMEMO_FL_USER0 + +#if USE_EXECUTED_CHECK +void rb_iseq_executed_check_dump(rb_iseq_t *iseq); +#endif + static inline const rb_iseq_t * rb_iseq_check(const rb_iseq_t *iseq) { @@ -419,6 +429,11 @@ rb_iseq_check(const rb_iseq_t *iseq) if (iseq->body == NULL) { rb_iseq_complete((rb_iseq_t *)iseq); } +#endif +#if USE_EXECUTED_CHECK + if ((iseq->flags & ISEQ_FL_EXECUTED) == 0) { + rb_iseq_executed_check_dump((rb_iseq_t *)iseq); + } #endif return iseq; } --
- iseq ãæã£ã¦ããæªä½¿ç¨ã®ãã©ã°ä¸ã¤ã iseq ãå®è¡ããããã¨ãããããå¤æããããã®ãã©ã°(
ISEQ_FL_EXECUTED
)ã¨ãã¦ä½¿ããããã«ãã ISEQ_FL_EXECUTED
ãã©ã°ãç«ã£ã¦ãªãå ´årb_iseq_check
ã§rb_iseq_executed_check_dump
ã¨ããé¢æ°ãå¼ãµrb_iseq_executed_check_dump
ã§ã¯ãã® iseq ã® path, first_lineno ã(ç°å¢å¤æ°IE_OUTPUT_PATH
ã§æå®ãã)ãã¡ã¤ã«ã«æ¸ãè¾¼ã
ãã®ããã« rb_iseq_check
ã«ããã¯ãã¤ã³ããä½ããã¨ã§ TracePoint ã¨ã¯æ¯ã¹ãã¾ã§ããªãã»ã©ã®ä½ã³ã¹ãã§å®è¡ããã iseq ãæ¢ãã¾ãã
ãã¡ãããã®ã³ã°ã®ã³ã¹ãã¯çºçããã®ã§æ³¨æããå¿
è¦ã¯ããã¾ãããä»çµã¿èªä½ã®ã³ã¹ãã¯å®è³ªã¼ãã«è¿ããã¨ãããã£ã¦ãã¾ãã
ãã®ããããå½ã¦ã Ruby ãå©ç¨ãããã¨ã§å®è¡ããã iseq ã®ãªã¹ããå¾ããã¨ãã§ãã¾ãã ä»åã¯æä½æ¥ã§ç¢ºèªããã対象ãæ¸ããããã®ãã®ãªã®ã§ãããããå½ã¦ã ruby ã§ãã¹ããå®èµ°ããããã®ãã°ãå©ç¨ãããã¨ã«ãã¾ããã以ä¸ã®ãããªå¤§éã®ãã°ãåãããã®ã§ããããå¦çãã¦å®é使ããã¦ã gem ã®ãªã¹ããä½æã§ãã¾ãã
.../2.4.3/lib/ruby/gems/2.4.0/gems/rspec-expectations-3.7.0/lib/rspec/matchers/built_in/has.rb:46 .../2.4.3/lib/ruby/gems/2.4.0/gems/rspec-expectations-3.7.0/lib/rspec/matchers/built_in/has.rb:58 .../2.4.3/lib/ruby/gems/2.4.0/gems/rspec-expectations-3.7.0/lib/rspec/matchers/built_in/has.rb:71 .../2.4.3/lib/ruby/gems/2.4.0/gems/rspec-expectations-3.7.0/lib/rspec/matchers/built_in/has.rb:63 .../2.4.3/lib/ruby/gems/2.4.0/gems/rspec-expectations-3.7.0/lib/rspec/matchers/built_in/has.rb:67 .../2.4.3/lib/ruby/gems/2.4.0/gems/capybara-2.13.0/lib/capybara/node/matchers.rb:245 .../2.4.3/lib/ruby/gems/2.4.0/gems/capybara-2.13.0/lib/capybara/node/matchers.rb:3
ä¾åãã¦ãã gem ã®ãªã¹ã㯠Bundler::LockfileParser
ãå©ç¨ããã¨ç°¡åã«å¾ããã¾ãã
# ããã¸ã§ã¯ã root require "bundler" lockfile_parser = Bundler::LockfileParser.new(File.read("Gemfile.lock")) lockfile_parser.specs.map(&:name)
ãã®ä½¿ç¨ããã gem ã®ãªã¹ãã¨ä¾åãã¦ãã gem ã®ãªã¹ããããå¾è ããåè ãå¼ãç®ãããã¨ã§ã ä¾åãã¦ããã使ç¨ããã¦ãªã gem ã®ãªã¹ããä½ãã¾ãã
ææ
ç¾å¨ãcookpad ã®ã¡ã¤ã³ã¬ãã¸ããªã«ã¯1ã¤ã® mountable engine ãå ±æãã 5ã¤ã®ããã¸ã§ã¯ããããã¾ãã ãã®5ã¤ã®ããã¸ã§ã¯ãã対象ã«ä¸è¨ã®ããããå©ç¨ãã¦ä½ãåºããæªä½¿ç¨ gem ã®ãªã¹ããä½æããå¿ è¦ã®ãªããã®ããªããä½æ¥ãé²ãã¾ããã
çµæã¨ãã¦ãã¹ã¦ã®ããã¸ã§ã¯ãããæªä½¿ç¨ã® gem ã 41åè¦ã¤ããã¾ããã ããããåé¤ãããã¨ã§ãä¾åãã¦ãã gem ã®æ°ãå¤§å¹ ã«æ¸ãããã¨ãã§ãã¾ããã ããã« require ãããã¡ã¤ã«ã®æ°ã大éã«æ¸ã£ããããã¢ããªã±ã¼ã·ã§ã³ã®èªã¿è¾¼ã¿æéãæ大1ç§ç¨åº¦éããªãã¾ããã
ã¾ã¨ã
Ruby ã® lazy loading ã¨ããä»çµã¿ãå©ç¨ãã¦æªä½¿ç¨ã® gem ãæ¢ãæ¹æ³ãç´¹ä»ãã¾ããã ãã®æ¹æ³ã¯ä½¿ç¨ããã¦ãªãã³ã¼ããæ¢ãã®ã«ä»¥ä¸ã®ãããªå©ç¹ãæã£ã¦ãã¾ãã
- ããã¸ã§ã¯ãå¥ã«ã³ã¼ããæ¸ãå¿ è¦ããªãã®ã§ã©ã®ããã¸ã§ã¯ããããç°¡åã«å©ç¨ãããã¨ãã§ãã
- åçã«çæãããã¡ã½ãããããç¨åº¦è¿½è·¡ãã§ãã
- ä½ã³ã¹ãã«ã³ã¼ãã®ä½¿ç¨ç¶æ³ãåãã
ç¹ã«ä¸çªç®ãéè¦ã ã¨æã£ã¦ãã¦ãæ¬çªã®ãµã¼ãã¹ãã使ããã¦ãªãä¾å gem ãããã¸ã§ã¯ãã³ã¼ããç°¡åã«è¿½è·¡ã§ããããããªããã¨æå¾ ãã¦ããã®ã§ã次åã«ãæå¾ ãã ããã
*1:æ£ç¢ºã«ã¯ï¼ã¤ãä½ããã¾ãããããã§ã¯èª¬æã®ããçç¥ãã¦ãã¾ã