ããã«ã¡ã¯ãMisocaéçºãã¼ã ã®é»æ(@kokuyouwind)ã§ãã
æè¿ã¯ã·ã£ããã¹ã®ã¤ãã³ãã·ããªãªææ³è¨äºãnoteã«ã¾ã¨ããããã¦ãã¾ãã
ð¨ æ¥ã«æ¬çªã®ã¬ã¹ãã³ã¹ã¿ã¤ã ãæªåãã話
Webã¨ã³ã¸ãã¢ã«ã¨ã£ã¦ããæ¬çªé害ãã¨ãã4æåã»ã©è¦ãããªããã®ã¯ãªãã§ãããã
æ¬çªé害ã»ã©ã§ã¯ãªãã«ãã¦ãããæ¥ãªã¬ã¹ãã³ã¹ã¿ã¤ã æªåãããã¾ãè¦ãããªãæååã§ãããã¾ããè¦ãããªãã¦ãåãããããã£ã¦ãããã§ããâ¦
ã¨ããããã§ãä»åã¯æ¬çªã¬ã¹ãã³ã¹ãæ¥ã«æªåããã¨ãã®è©±ã§ãããããã調ã¹ãçµæãå©ç¨ãã¦ãããªã¼ãã³ã½ã¼ã¹ããã¸ã§ã¯ããåå ã ã£ããã¨ããããPull Requestãéã£ãã®ã§ããã®æµããã¾ã¨ãã¦ã¿ããã¨æãã¾ãã
âï¸ ã¬ã¹ãã³ã¹ã¿ã¤ã æªåã®æ¤ç¥
Misocaã§ã¯ç£è¦ãã¼ã«ã¨ãã¦MackerelããAPMãã¼ã«ã¨ãã¦Skylightãå©ç¨ãã¦ãã¾ãã
æ¬çªã¬ã¹ãã³ã¹ã¿ã¤ã ã¯Mackerelã§ã¢ã©ã¼ããè¨å®ãã¦ããã平常å¤ãã大ããæªåããå ´åã¯Slackã«éç¥ãããããã«ãªã£ã¦ãã¾ãã
ãã®æ¥ã¯ãªãªã¼ã¹ä½æ¥ãè¡ã£ãç´å¾ãã¬ã¹ãã³ã¹ã¿ã¤ã æªåã®ã¢ã©ã¼ããé£ãã§ãã¾ãããæ°ã«ãªã£ã¦Skylightã確èªããã¨ãããé©ãã®äºæ ãã*1
ãªãªã¼ã¹ä»¥éã95ãã¼ã»ã³ã¿ã¤ã«ããã¡ããã¡ãæªåãã¨ãããï¼*2*3
𩹠å¿æ¥å¦ç½®
幸ãã¢ããªã±ã¼ã·ã§ã³ã使ããªããªãã»ã©ã®æªåã§ã¯ãªãã£ããããè½ã¡çãã¦å¿æ¥å¦ç½®ã«è¨ã¿ã¾ãã
Skylightã§è¡¨ç¤ºããæéããªãªã¼ã¹åã¨ãªãªã¼ã¹å¾ã§åãæ¿ãã¦æ¯ã¹ãçµæãã©ãããPDFçæå¦çã«ãããæéã伸ã³ã¦ãããããã¨ãããã¾ããã
ãªãªã¼ã¹å 容ãç²¾æ»ããã¨ãããã¡ããã©ãã®ã¿ã¤ãã³ã°ã§PDFçæå¦çã«é¢ä¿ããããªTTFunk gemã®æ´æ°ãå ¥ã£ã¦ãããã¨ãå¤æã*4
ããããããã ããã¨ã¢ã¿ãªãã¤ãã該å½ã³ããããå·»ãæ»ãã¦ãªãªã¼ã¹ãçµæãè¦äºã«ã¬ã¹ãã³ã¹ã¿ã¤ã ãå復ãã¾ããã
ðµï¸ââï¸ åå 調æ»
ã²ã¨ã¾ãæ¬çªã®åé¡ã¯è½ã¡çããããããªã«ãåå ã ã£ãã®ãã調æ»ãã¦ããã¾ãã
ã¾ãã¯ãã¼ã«ã«ç°å¢ã§ã®åç¾ç¢ºèªã以ä¸ã®ãããªã³ã¼ãã§ãã³ããã¼ã¯ãåããgemæ´æ°åã¨æ´æ°å¾ã¨ã§PDFçæã«ãããæéãå¢ãããã確èªãã¾ãã
10åç¹°ãè¿ãã¦ããã®ã¯ActiveRecordã¢ã¯ã»ã¹ãªã©PDFçæ以å¤ã®è¦å ãç¸å¯¾çã«å°ããããããã§ãã
require 'benchmark' Benchmark.bmbm do |x| x.report('generate') { 10.times { Misoca::PDF.generate(Invoice.first) } } end
çµæãttfunk 1.5.1ã®ã¨ãã¨æ¯ã¹ã¦ãttfunk 1.6.2.1ã§ã¯æ¦ã4åã»ã©é ããªããã¨ã確èªã§ãã¾ããã
æ¬çªã§ããèµ·ããªãåé¡ã ã¨èª¿æ»ãé£ããã®ã§ããããã¼ã«ã«ã§ãåç¾ãããã¨ãããã£ããã調æ»ããããããªãã¾ãããã
ç¶ãã¦Stackprofã使ã£ã¦ãããã¡ã¤ã«ãåããã©ã®ãããªå¦çã«æéãããã£ã¦ãããã調ã¹ã¦ããã¾ãã
require 'stackprof' StackProf.run(mode: :cpu, out: 'tmp/stackprof-cpu-myapp.dump', raw: true) do 10.times { Misoca::PDF.generate(Invoice.first) } end
stackprof tmp/stackprof-cpu-myapp.dump
ã§ç¢ºèªããçµæã以ä¸ã®ããã«ActiveSupport::CompareWithRange#cover?
ã50%以ä¸ã®æéã使ã£ã¦ãããã¨ããããã¾ããã
================================== Mode: cpu(1000) Samples: 13617 (0.71% miss rate) GC: 1461 (10.73%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 7765 (57.0%) 7765 (57.0%) ActiveSupport::CompareWithRange#cover? 1138 (8.4%) 1138 (8.4%) (marking) 8609 (63.2%) 844 (6.2%) TTFunk::Table::OS2.group_original_code_points_by_bit 322 (2.4%) 322 (2.4%) String#unpack 322 (2.4%) 322 (2.4%) (sweeping) ...
TTFunk::Table::OS2.group_original_code_points_by_bit
ãTOTALã§ã¯å¤ãã®æéã使ã£ã¦ãããSAMPLESã¯å°ãªããªã£ã¦ãã¾ãã
ãã®ãã¨ãããTTFunk::Table::OS2.group_original_code_points_by_bit
ããActiveSupport::CompareWithRange#cover?
ãå¼ã³åºãã¦ããç®æãéãã®ã ããã¨æ¨æ¸¬ãããã¨ãã§ãã¾ããã
å®éã«TTFunk::Table::OS2.group_original_code_points_by_bitã®ã³ã¼ãã確èªããã¨ã以ä¸ã®ããã«r.cover?
ãç¹°ãè¿ãå¼ã³åºãã¦ãã¾ãã
os2.file.cmap.unicode.first.code_map.each_key do |code_point| # find corresponding bit range = UNICODE_RANGES.find { |r| r.cover?(code_point) }
ãã®å¦çã¯1.6.0ã§è¿½å ããããã®ã§ãããããã«ããã©ã¼ãã³ã¹ä¸ã®åé¡ãããããã§ããã
ð Issueãç«ã¦ã
åé¡ãææ¡ã§ããããæ¢åã®IssueãPull Requestã確èªãã¾ããããã©ãããã¾ã å ±åããã¦ããªãããã§ããã
æ°ããIssueãç«ã¦ããã¨ããã§ããããã®ããã«ã¯æå°éã®åç¾ã³ã¼ããä½ããttfunkèªä½ã®åé¡ã§ãã確証ãå¾ã¦ããããã¨ããã§ãã
PDFçæã®å
é¨å¦çããã©ã®ããã«ttfunkãå¼ã³åºããã¦ãããããããªãã£ããããstackprof --graphviz tmp/stackprof-cpu-myapp.dump > tmp/stackprof.dot
ã§dotãã¡ã¤ã«ãä½æããdot -T pdf -o tmp/stackprof.pdf tmp/stackprof.dot
ã§PDFåãã¦å¼ã³åºãã°ã©ããè¦ã¦ã¿ã¾ããã*5
ã©ããããTTFunk::Subset::Base#encode
ãå¤é¨ã¨ã®ã¤ã³ã¿ã¼ãã§ã¤ã¹ã®ããã§ãã
specãã¡ã¤ã«ãåèã«ãã¤ã¤ããã®å¦çãå¼ã³åºãåç¾ã³ã¼ããæ¸ãã¦ã¿ã¾ãã
require 'ttfunk' require 'benchmark' # English Font file = TTFunk::File.open("DejaVuSans.ttf") # Japanese Font # file = TTFunk::File.open("GenShinGothic-Normal.ttf") subset = TTFunk::Subset.for(file, :unicode) Benchmark.bmbm { |x| x.report('encode') { 10.times { subset.encode } } }
æ¥æ¬èªãªã©ã®ãã«ããã¤ãæåã§ããåç¾ããªãåé¡ãããããªãã®ã§ããã©ã³ããè±åãã©ã³ãã¨æ¥æ¬èªãã©ã³ãã®2種é¡ã§ç¢ºèªã§ããããã«ãã¦ãã¾ãã
ãã®ã³ã¼ããç¨ãã¦1.5.1ã¨1.6.2.1ã§ãã³ããã¼ã¯ãåã£ãã¨ãããè±åãã©ã³ãã»æ¥æ¬èªãã©ã³ããåããå®ã«30åç¨åº¦é ããªã£ã¦ãããã¨ããããã¾ããããã¯ãæ¥æ¬èªãã©ã³ãã®ã»ããé·æéããããããè±åãã©ã³ããæ±ãæµ·å¤ã§ã¯ããã©ã¼ãã³ã¹ã®åé¡ã«æ°ã¥ããªãã£ãã®ããããã¾ããã
ç¡äºttfunkã®ã¿ã§åç¾ã³ã¼ããä½ãè¨æ¸¬ãã§ãããããIssueã¨ãã¦å ±åãããã¨ãã§ãã¾ããã
ð ä¿®æ£Pull requestãéã
ãã¾ãã«é£ããããªåé¡ã§ããã°æåºãã§ãã¾ããããä»åã¯æ¯è¼çèªããããªã³ã¼ãã ã£ããããèªåã§ç´ããªããæ¤è¨ãã¦ã¿ããã¨ã«ãã¾ãã
åé¡ã®ç®æã§ã¯os2.file.cmap.unicode.first.code_map
ã®åãã¼ã«ã¤ãã¦eachãå¼ã³åºããUNICODE_RANGES
ã®ä¸ããr.cover?(code_point)
ãæºãããã®ãæ¢ãåºãã¦ãã¾ãã
ããããã®è¦ç´ æ°ãN
,M
ã¨ããã¨ããã®å¦ç㯠Î(N * M)
ã®è¨ç®éã«ãªãã¾ããM
ã¯200å¼±ã®åºå®å¤ã§ãããããã§ãå¹çã®æªãè¨ç®ã«è¦ãã¾ããã
ç´ ç´ã«æãã¤ãã®ã¯UNICODE_RANGES
ãæ¢ç´¢æ¨ã«ãããã¨ã§ãããã ãUNICODE_RANGES
ã¯Range
ã®é
åã®ããé常ã®æ¢ç´¢æ¨ã§ã¯ãªãåºéæ¨ã使ããã¨ã«ãªãã¾ããRubyæ¨æºã§ãµãã¼ããããªããã¼ã¿æ§é ã®ããä¾ågemãå¢ãããèªåã§å®è£
ãããã«ãªãã¾ãããããããå°ã
大ããªå¤æ´ã«ãªãã¾ãããªã¼ãã³ã½ã¼ã¹ããã¸ã§ã¯ãã¸ã®Pull Requestã¨ãã¦ã¯ãã§ããã°é¿ãããã¨ããã§ãã
ããã§çºæ³ãå¤ãã¦ããUNICODE_RANGES
ãã¨ã«ããã®ç¯å²ã«å«ã¾ããcode_map
ãã¼ãã¾ã¨ãã¦è¦ã¤ãåºããã¨ãããã©ãã§ãããããcode_map
ãæ´åãã¦ããå ´åãåãUNICODE_RANGES
ã«å«ã¾ãããã¼ã¯å¿
ãåºã¾ã£ã¦ä¸¦ã¶ãããå¹çè¯ãå¦çã§ãããã§ããã¾ãUNICODE_RANGES
ã¯äºãã«éè¤ããªãç¯å²ã§ãããããä»ã®UNICODE_RANGES
ã«å«ã¾ãã¦ãããã®ã以éã®å¤å®ããé¤å¤ãããã¨ãã§ãã¾ãã
ãã®æ¹éã§å®è£
ãã¦ã¿ãã¨ãããã¾ãåãããããPull Requestãåºããã¨ã«ãã¾ãããè¨ç®éã¯Î(max(N, M))
ã¾ã§æ¹åãã¦ããããã³ããã¼ã¯ã8åã»ã©æ©ããªã£ã¦ãã¾ãã
ð¬ ææ³
ä»åã¯æ¬çªã§ããã©ã¼ãã³ã¹æªåã«æ°ã¥ãã¦ããåå ã調æ»ãããªã¼ãã³ã½ã¼ã¹ããã¸ã§ã¯ãã«Issueãç«ã¦ã¦Pull Requestãéãã¾ã§ã®æµããã¾ã¨ãã¦ã¿ã¾ããã
å®çªã§ããStackprofãç¨ãããããã¡ã¤ã«åæã¯å¤§åã§ãããã¾ãã¿ããªã®ã³ã³ãã¥ã¼ã¿ãµã¤ã¨ã³ã¹èªæ¸ä¼ã§èªãã ã°ããã®è¨ç®éãã¢ã«ã´ãªãºã ã®è©±ããã£ããæ´»ãããã®ãè¯ãã£ãã§ãã
ä»å¾ãå©ç¨ãã¦ãããªã¼ãã³ã½ã¼ã¹ããã¸ã§ã¯ãã®åé¡ãè¦ã¤ããã¨ãã¯ãIssueã§å ±åãã¦ç´ãããã®ã¯Pull Requestãåºãã¦ããããã¨æãã¾ãã
ð¢ 宣ä¼
Misocaã§ã¯ããã©ã¼ãã³ã¹ã®åé¡ãæ¹åãããã¨ã³ã¸ãã¢ãåéãã¦ãã¾ãã
*1:話ãç°¡åã«ããããã«çç¥ãã¾ããããå®ã¯æåã®è² è·ã«ã¾ããã¦ãã¾ããå½æ¥ã¯ã¢ã¯ã»ã¹éã®åé¡ã ã¨æã£ã¦ãã¾ãããç¿æ¥ãæ°å¤ãå復ããªãã£ãããæªããã§èª¿æ»ããã³ã¼ãèµ·å ã®åé¡ã§ãã£ããã¨ããããã¾ããã
*2:95ãã¼ã»ã³ã¿ã¤ã«ã¯ãã¬ã¹ãã³ã¹ã¿ã¤ã ãã¹ã¦ãçãé ã«ä¸¦ã¹ã¦95%ç®ã®å¤ãåãåºãããã®ã§ããSkylightã®Typical responseã¯ä¸å¤®å¤(50ãã¼ã»ã³ã¿ã¤ã«)ããProblem Responseã¯95ãã¼ã»ã³ã¿ã¤ã«ã表示ãã¦ãã¾ãã
*3:ã°ã©ãã®ç¸¦è»¸ã¯å ·ä½çãªããã©ã¼ãã³ã¹ã®æ å ±ã«ãªã£ã¦ãã¾ããããæå³çã«ä¼ãã¦ãã¾ãã
*4:話ãç°¡åã«ããããã«çç¥ãã¾ããããå®éã«ã¯å¥ã®gemã®æ´æ°ã«ä¼´ããä¾åãã¦ããttfunkãæ´æ°ããã¦ãã¾ãããåå 調æ»ã®éã¯ããã®åãåãã§ããªãè¦å´ãã¾ããã
*5:flamegraphã§ãåããããªã³ã¼ã«ã¹ã¿ãã¯ãè¦ãã¾ãããã¹ã¯ãªã¼ã³ã·ã§ããã§è²¼ã£ãã¨ãã«ç´°ãããã¦è¦ã¥ãããããä»åã¯graphvizã使ã£ã¦ãã¾ãã