ãã®è¨äºã¯Â Akatsuki Advent Calendar 2019 1æ¥ç®ã®è¨äºã§ãã
ã¯ããã«
ã¢ã«ããã§ã¯ Ruby on Rails ã使ã£ãã²ã¼ã ãµã¼ããéçºã»éç¨ãã¦ãã¾ããã²ã¼ã ã®ä½é¨ãåä¸ããããã«ãã¬ã¹ãã³ã¹ã¿ã¤ã ã¯ä¸ã¤ã®éè¦ãªè¦ç´ ã¨ãªãããã種ã ã®ããã©ã¼ãã³ã¹ãã¥ã¼ãã³ã°ãè¡ãªã£ã¦ãã¾ããä»åã¯ãã®ä¸ä¾ã¨ãã¦ãç°å¢å¤æ°ã1ã¤è¨å®ããã ãã§ãããAPIã®ã¬ã¹ãã³ã¹ã¿ã¤ã ã10%ãæ¹åããä¾ããç´¹ä»ãã¾ãã
TL;DR
å¤æ°ã®æå»ãå«ãã¬ã³ã¼ããæ±ã Ruby on Rails ãµã¼ãã§ã¯ã TZ ç°å¢å¤æ°ãè¨å®ãããã¨ã§ãããã©ã«ãã¿ã¤ã ã¾ã¼ã³è¨å®ãã¡ã¤ã« /etc/localtime ã¸ã®ã¢ã¯ã»ã¹ãæ¸ããé«éåãå³ããããããã¾ããã
å¹æ㯠Time ãªãã¸ã§ã¯ã1åãããæ°Î¼sã®ç縮ã¨ãã£ããªã¼ãã¼ã§ãããããªãç©ããã°å±±ã¨ãªããæ°ååã®ã¬ã³ã¼ããå¦çããAPIã§ã¯ã¬ã¹ãã³ã¹ã¿ã¤ã ã10%è¿ãæ¹åããä¾ãããã¾ããã
ãã£ãã
ããæ¥ãã¢ãã¤ã«ã²ã¼ã ã®APIãµã¼ãã®è² è·ãã¹ãã大ããã®ã¤ã³ã¹ã¿ã³ã¹ãµã¤ãºã®ãµã¼ãã§å®æ½ããã¨ãããå°ããæ§æã®ãµã¼ãã§ã¯åé¡ãªãã£ãAPIã®ã¬ã¹ãã³ã¹ã¿ã¤ã ãããªããæ°ç§ããæ°10ç§ã¨ãã¨ãã§ããªãé
ããªãã¨ããäºè±¡ãèµ·ãã¾ããã
ãã®æã®ã·ã¹ãã ã®ç¶æ
ã top ã確èªãã¦ã¿ãã¨ãCPU使ç¨çã® user ã¯æ°%ãªã®ã«ãé¢ããããsys ã 95% ã¨ãã£ãé«ãå¤ã示ãã¦ãããã¢ããªã±ã¼ã·ã§ã³ã§ã¯ãªã OS (Linux) å¨ãã®åé¡ã§ããããã§ããã
ç°å¢
åé¡ãçºçããç°å¢ã¯ãAWS EC2ã® c5.9xlarge ã¤ã³ã¹ã¿ã³ã¹ (36 core) ã®ä¸ã«Docker㧠ruby:2.5 ã³ã³ãã(Debian Stretchãã¼ã¹)ãç«ã¦ããã®ä¸ã§Railsãµã¼ãã¼ãåããã¦ãããããã«å¤æ°ä¸¦åã§HTTPãªã¯ã¨ã¹ããéãè¾¼ãã§ãããã¨ããç¶æ³ã§ãã
Railsã®ãµã¼ãã¨ãã¦ãunicorn ãè¤æ°ããã»ã¹åãã¦ãããåããã»ã¹ã¯ã·ã³ã°ã«ã¹ã¬ããã§åä½ããæ§æã§ããã¤ã¾ããã¹ã±ã¼ã©ããªãã£ã®åé¡ã®åå ã«ãªãããã Ruby ã® GIL (ã°ãã¼ãã«ã¤ã³ã¿ããªã¿ããã¯) ã®ããã§ã¯ããã¾ããã
詳ããåæãã¦ã¿ã
ãã®ã»ã¯ã·ã§ã³ã§ã¯ã Linux ã«ã¼ãã«ã Ruby ã®åä½ã調ã¹ãå 容ã説æãã¦ããã¾ããä½ã¬ã¤ã¤ã¼ã«è¸ã¿è¾¼ã話ã«ãªãã®ã§ãæ©ãçµæãç¥ãããã¨ããæ¹ã¯ã次ã®ã»ã¯ã·ã§ã³ã®ã対çã¨ãã¾ãå¹æãã¾ã§èªã¿é£ã°ãã¦æ§ãã¾ããã
ãããã¡ã¤ã©ãããã¦ã¿ã
ãããã£ãã¨ãã«ã¯ãã©ãã§CPUã使ç¨ããã¦ãããããããã¡ã¤ã©ã§èª¿ã¹ã¦ã¿ãã¨æå¹ã§ããããã§ã¾ãã Linux ã®ãããã¡ã¤ã©ã§ãã perf ã使ç¨ãã¦ã¿ã¾ãã
åé¡çºçä¸ã« sudo perf top -g ãå®è¡ããã¨ãCPU ã使ããã¦ããå ´æã®ãããã¡ã¤ã«ã C ã®é¢æ°åä½ã§ãªã¢ã«ã¿ã¤ã ã«è¡¨ç¤ºããã¾ãã
以ä¸ã¯95%以ä¸ãå ããé«ã __xstat64 ããä¸ä½ã®é¢æ°ãå±éãã¦ãã£ãã¨ããã§ãããªã __xstat64 ã¯ãã¡ã¤ã«ã®å¤æ´æ¥æãªã©ã®æ å ±ãåå¾ããglibcã®é¢æ°ã§ãã
Samples: 30K of event 'cycles', Event count (approx.): 32976632083 Children Self Shared Object Symbol - 95.21% 0.02% libc-2.24.so [.] __xstat64 - __xstat64 - 95.04% entry_SYSCALL_64 - do_syscall_64 - 94.14% __do_sys_newstat - 94.03% vfs_statx - 92.21% filename_lookup - 92.14% path_lookupat - 66.85% link_path_walk.part.39 - 63.60% walk_component - 22.86% lookup_fast - 22.75% __d_lookup + _raw_spin_lock - 21.11% path_parent_directory - 19.89% dget_parent - lockref_get_not_zero + _raw_spin_lock + 1.23% dput - 19.53% dput + 18.02% _raw_spin_lock 1.38% lockref_put_return + 3.00% inode_permission - 22.98% walk_component - 20.49% dput + 19.17% _raw_spin_lock 1.14% lockref_put_return + 2.41% lookup_fast + 1.70% trailing_symlink + 0.57% terminate_walk + 0.88% path_put
詳細ã¯å²æãã¾ãããpath_lookupat é¢æ°ãå§ãã主㫠Linux ã«ã¼ãã«å
ã®ãã¡ã¤ã«ãã¹ã辿ãé¢æ°ç¾¤ã§æéãæ¶è²»ãã¦ããããã§ããããã«è¾¿ã£ã¦ããã¨ã主è¦å ã¨ãã¦ããããªã¨ãã㧠_raw_spin_lock ãåºã¦ãã¾ãããããã¯ã Linux ã«ã¼ãã«ã®ã¹ãã³ãã㯠(ä»ã®CPUã¨æä»ãåããã¾ã§ç¡éã«ã¼ãããªããå¾
ã¤ããã¯æ¹å¼) ã®é¢æ°ã§ã1ã¤ã®ã³ã¢ããåæã«å¦çãè¡ãã¾ãããä½ããã®åããªã½ã¼ã¹ã®åãåããå
¨ã³ã¢ã§çºçããã³ã¢æ°ãå¤ãåãããæ¿ãã競åãçºçããçµæãã¢ããªã±ã¼ã·ã§ã³ã®å¦çãé
ã
ã¨ãã¦é²ã¾ãªãã»ã©ã«æ§è½ãä½ä¸ãã¦ãã¾ã£ãã¨èãããã¾ãã
ã§ã¯ä¸ä½ä½ã奪ãåã£ã¦ããã®ã§ãããã?
ã·ã¹ãã ã³ã¼ã«å¼ã³åºããè¦ã¦ã¿ã
ã¢ããªã±ã¼ã·ã§ã³ããã®æä½ããã¦ããã®ãã調ã¹ã¦ã¿ã¾ãã
strace -p <rubyã®PID>
ãå®è¡ãã¦ä½ã®ã·ã¹ãã ã³ã¼ã«å¼ã³åºãã¦ãããã調ã¹ã¦ã¿ãã¨ãã©ã®ããã»ã¹ãã
stat("/etc/localtime", ...) stat("/etc/localtime", ...) stat("/etc/localtime", ...) ...
ã¨ãã²ããã /etc/localtime ã¨ãããã¡ã¤ã«ã«å¯¾ã㦠stat ã·ã¹ãã ã³ã¼ã«ãå¼ã³åºãã¦ãããã¨ããããã¾ãããåããã¡ã¤ã«ã«å¯¾ãã¦å ¨ã³ã¢ã§ããã¯ãåããã£ã¦ãããããæ¿ãã競åãçºçãã¦ããããã§ããã
ãã®ãã¡ã¤ã«ã¯ãã·ã¹ãã ã®ããã©ã«ãã®ã¿ã¤ã ã¾ã¼ã³æ å ±ãæ ¼ç´ãããã®ã§ãã
ãã¡ã¤ã«ã¨è¨ãã¾ããããæ£ç¢ºã«ã¯rubyã³ã³ããã§ã¯ã·ã³ããªãã¯ãªã³ã¯ã«ãªã£ã¦ãã¾ãã
UTCãããã©ã«ãã¿ã¤ã ã¾ã¼ã³ã¨ãã¦ä½¿ç¨ãã¦ãã¾ãã®ã§ããªã³ã¯å 㯠/usr/share/zoneinfo/Etc/UTC ãæãã¦ãããããã«ãããã·ã³ããªãã¯ãªã³ã¯ã§ ../UCT ãæãã¦ãã¾ãã
ã¾ãåæ§ã« ltrace ãã¢ã¿ãããã¦ã¿ãã¨ãtz_set, localtime_r ãªã©ã®æå»é¢é£ã®é¢æ°ãã²ãããå¼ã³åºããã¦ãã¾ããããã®ä¸ã§å ã»ã©ã® stat ã·ã¹ãã ã³ã¼ã«ãå¼ã³åºããã¦ãã¾ãã
stat ã·ã¹ãã ã³ã¼ã«ã¨ãããã¨ã¯ããã®ãã¡ã¤ã«ã®å 容ãèªãã§ããããã§ã¯ããã¾ãã*1ããããããããã¤ã®éã«ãããã©ã«ãã®ã¿ã¤ã ã¾ã¼ã³ãååããå¤æ´ããã¦ãããããããªãã®ã§ãåèªã¿è¾¼ã¿ãå¿ è¦ãã©ããã調ã¹ãããã«ãæ´æ°æ¥æã調ã¹ã¦ããã®ã§ãã( â glibc ã® __tzfile_read() )
Ruby on Railsã¯ãã®æä½ããã¦ããã®ãï¼
ã§ã¯ããªãã§ãããªã« localtime_r ãå¼ã°ãã¦ããã®ã§ããããï¼
å®ã¯ãè² è·ãã¹ãã®ã·ããªãªã®ä¸ã«ãæ°å件ã®ã¬ã³ã¼ãã MySQL ããåå¾ãã¦èªã¿è¾¼ã API ãããã¾ããããããã®ã¬ã³ã¼ãã«ã¯ããããæ°åã®æ¥æãå«ã¾ãã¦ãã¾ãããå ãã¦ãActiveRecordã®æ
£ä¾ã«å¾ã£ã¦ãåã¬ã³ã¼ãã«ã¯ created_at, updated_at ã«ã©ã ããããä½æã»æ´æ°æå»ãæ ¼ç´ãã¦ãã¾ãã
ããã¦ãããã ActiveRecord ã Time(WithZone) ã¯ã©ã¹ã¨ãã¦ã¤ã³ã¹ã¿ã³ã¹åããéã«ã localtime_r ãå¼ã³åºããã¦ããã®ã§ããã
localtime_r 㯠Time.new ãã¨ã«1åå¼ã°ããã ãã§ã¯ãªãã£ã
ãããã localtime_r 㯠Time ã®ã¤ã³ã¹ã¿ã³ã¹åã§ä¸åå¼ã³åºãããã ãã§ã¯ããã¾ããã§ããã
strace ruby -e 'puts; p Time.new'
ãªã©ã¨å®è¡ãã¦ã·ã¹ãã ã³ã¼ã«ã®å¼ã³åºããæ¹ãè¦ã¦ã¿ãã¨ã以ä¸ã®ããã«ãªãã¾ããã*2
- Time.new â statã1åå¼ã°ãã
- Time.new(2019,7,1,0,0,0,"+09:00") â ãªããstatã2åå¼ã°ãã
- Time.new(2019,7,1,0,0,0) â ãªããstatã4åãããå¼ã°ãã(timezoneãåå¾ãããã?)
Ruby ã® time.c ãè¦ã¦ã¿ãã¨ã find_time_t é¢æ°ã«ã¦ã·ã¹ãã ã® localtime_r ã«æå®æå»ã®2æéåå¾ã®æå»ãä¸ãã¦ãä½ãè¿ã£ã¦ãããã調ã¹ã¦ãã¾ããããã¯ã©ããããå¤æéã®åãæ¿ããã®éã«ã¯åä¸ã®æå»ã2åããå ´åãããã®ã§ãããããã¨ãã«å¿ ã決ã¾ã£ãå´ãè¿ãããã®å¦çã®ããã§ããã
ããã«ãã¦ãããã®ã³ã¼ãã«ã¯å¤æ°ã® #ifdef ããããRuby ãæ§ã ãªç°å¢ã§åããããã®è¦å´ã窺ããã¾ãâ¦â¦ã
対çã¨ãã¾ãå¹æ
man tzset ãè¦ã¦ã¿ãã¨æ¸ãã¦ããã®ã§ããã ç°å¢å¤æ° TZ ã«å¤ãè¨å®ãã¦ããã°ã /etc/localtime ã¯èªã¾ããªããªãããã®åé¡ãçºçããªããªãã¾ãã
ããã§ãä¾ãã° TZ=UTC ã¨è¨å®ããã¨ã stat çã®ãã¡ã¤ã«ã¢ã¯ã»ã¹ã¯ãªããªãã¾ãã
ããã¯ä»åãã£ããã¨ãªã£ãè² è·ãã¹ãã§è¦ããããããªå¤§è¦æ¨¡ãµã¼ãã§ã®ã¹ã±ã¼ã©ããªãã£ã®è§£æ±ºã¯ãã¡ããã§ããããã¨ãå°ããªãµã¼ãã§ãã£ã¦ããã·ã¹ãã ã³ã¼ã«ã®çºè¡èªä½ããªããªããã¨ã§æ§è½åä¸ãæå¾ ã§ãã¾ãã
å ·ä½çã«ãå ã»ã©ã®ruby:2.5.1ã³ã³ããå ã§ãã·ã³ã°ã«ããã»ã¹ã®ã¿ã§è©¦ãã¦ã¿ã¾ãããã
$ irb irb(main):001:0> t = Time.now; 100000.times { Time.new(2019) }; Time.now - t => 1.027663412 # ç°å¢å¤æ°ãè¨å® irb(main):002:0> ENV['TZ'] = 'UTC' => "UTC" irb(main):003:0> t = Time.now; 100000.times { Time.new(2019) }; Time.now - t => 0.135658217
ãªãã¨7ã8åã®é«éåã§ã!
Linuxãã£ã¹ããªãã¥ã¼ã·ã§ã³ã«ãã£ã¦ã¯ã /etc/localtime ã¯ã·ã³ããªãã¯ãªã³ã¯ã§ãªãå®ãã¡ã¤ã«ã§ãããã¨ãããã¾ãã
ä¾ãã°Amazon Linux 2ã§ã¯ /etc/localtime ã¯å®ãã¡ã¤ã«ã¨ãªã£ã¦ãã¾ããããã®ã±ã¼ã¹ã§ã測å®ãã¦ã¿ã¾ããã
irb(main):001:0> t = Time.now; 100000.times { Time.new(2019) }; Time.now - t => 0.57196811
TZ ãè¨å®ããã¨å ã®ä¾ã¨åç¨åº¦ã®æéã«ãªããç´4åã®æ¹åã¨ãªãã¾ãããã·ã³ããªãã¯ãªã³ã¯ã®ãã¹ã辿ããªãã¦æ¸ãåã軽微ãªå½±é¿ã¨ãªãããã§ãã
ãã¡ãããã㯠Time.new ã®ã¿ã®åçã§ãã£ã¦ã1å1åã¯é«ã æ°Î¼sã¨ãã£ããªã¼ãã¼ã®å¹æã§ããããããæ°åã¬ã³ã¼ããæ±ããããªAPI*3ã ã¨ãç©ããç©ãã£ã¦APIå ¨ä½ã®ã¬ã¹ãã³ã¹ã¿ã¤ã ã10%ç¨åº¦ãæ¹åããã±ã¼ã¹ãå®éã«ããã¾ããã
ã¾ã¨ã
Ruby on Railsã§å¤§éã®æéãå«ãã¬ã³ã¼ããæ±ãéã TZ ç°å¢å¤æ°ãè¨å®ããã¦ããªãã¨ãTime ãªãã¸ã§ã¯ãã®åæåã®ãã³ã«è¤æ°å /etc/localtime ã«å¯¾ã㦠stat ã·ã¹ãã ã³ã¼ã«ãå¼ã³åºããããããæ§è½ãä½ä¸ãã¾ããç¹ã« /etc/localtime ãã·ã³ããªãã¯ãªã³ã¯ã§ããå ´åããã³ã¢æ°ã®å¤ããµã¼ãç°å¢ã§ã¯ã«ã¼ãã«å ã®ã¹ãã³ããã¯ã®ããã«å½±é¿ãé¡å¨åãããããªãã¾ãããã®ç¾è±¡ã¯ç¹ã«Ruby 2.5ã¾ã§ã«ããã¦é¡èã§ãã
TZ ç°å¢å¤æ°ãé©åãªå¤ã«è¨å®ãããã¨ã§ããããé¿ãã¦ãæå»ãæ±ãå¦çãé«éåãããã¨ãã§ãã¾ãã
Â
ä»å¾ã®Rubyã¸ã®ãã£ã¼ãããã¯
ãã¦ãããã§æ¬åé¡ã¯ä¸æ®µè½ãªã®ã§ããã ããããæéããããªãã¨ãRubyèªä½ã§é«éã«åä½ãã¦ããããå¬ããã§ããããã¢ã«ããã«æè¡é¡§åã¨ãã¦å ãã£ã¦é ããå°å´è³åºããã«ããã®ç¾è±¡ã«ã¤ãã¦Rubyã®ã¢ããã¹ããªã¼ã ã§æ ¹æ¬çãªæ¹åãã§ããã®ã§ã¯ãªããã¨ãã³ãã¥ããã£ã§è°è«ãã¦ããã ãã¦ãã¾ãã
 詳ããã¯å¥ã®è¨äºã§ãç´¹ä»ãããã¨æãã¾ãã®ã§ãä¹ããæå¾
ãÂ
â è¨äºãå
¬éããã¾ãã! ãã²ä½µãã¦ãèªã¿ãã ããã
*1:ãã¡ã¤ã«ã®å 容ã¯ãèµ·åç´å¾ã«1度ã ãèªã¿è¾¼ã¾ãã¦ãã¾ãã
*2:ãªãããã®æåã¯Ruby 2.6ã§å°ãå¤æ´ãããstat(2)ã®å¼ã³åºã㯠1åã¨ãªã£ã¦ããããã§ãã
*3:ã²ã¼ã ã®éç¨ãç¶ãã¦ããã«ã¤ãã¦ãã¼ã¿ãå¢ãã¦ãããæ°å件ã®ã¬ã³ã¼ãå¦çã®èªã¿è¾¼ã¿ãå¿ è¦ã«ãªããã¨ã¯ã¾ã¾ããã¾ãã