Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Fixing a performance issue with the mustache gem
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Dominic Baggott
October 12, 2015
Programming
0
520
Fixing a performance issue with the mustache gem
@alicebartlett and I gave this talk at LRUG, on Monday 12th October 2015.
Dominic Baggott
October 12, 2015
Tweet
Share
Other Decks in Programming
See All in Programming
そのAIレビュー、レビューしてますか? / Are you reviewing those AI reviews?
rkaga
6
4.4k
Pythonではじめるオープンデータ分析〜書籍の紹介と書籍で紹介しきれなかった事例の紹介〜
welliving
3
870
Vibe codingでおすすめの言語と開発手法
uyuki234
0
210
なぜSQLはAIぽく見えるのか/why does SQL look AI like
florets1
0
430
Fluid Templating in TYPO3 14
s2b
0
120
AIエージェント、”どう作るか”で差は出るか? / AI Agents: Does the "How" Make a Difference?
rkaga
4
2k
OCaml 5でモダンな並列プログラミングを Enjoyしよう!
haochenx
0
110
MUSUBIXとは
nahisaho
0
120
Oxlintはいいぞ
yug1224
5
1.2k
高速開発のためのコード整理術
sutetotanuki
1
370
余白を設計しフロントエンド開発を 加速させる
tsukuha
7
2.1k
CSC307 Lecture 05
javiergs
PRO
0
490
Featured
See All Featured
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
170
Git: the NoSQL Database
bkeepers
PRO
432
66k
30 Presentation Tips
portentint
PRO
1
210
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
54
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.1k
Embracing the Ebb and Flow
colly
88
5k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
1
430
A Tale of Four Properties
chriscoyier
162
24k
Marketing to machines
jonoalderson
1
4.6k
A better future with KSS
kneath
240
18k
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
76
Transcript
fixing a performance issue with the mustache gem (a bit
less boring than it sounds)
@alicebartlett & @evilstreak
We used to work at GDS, on gov.uk
In this talk: - Adding a feature - Finding a
bug - working around the bug - fixing the bug
GOV.uk is the best place to find government services and
information
These are Manuals They are multi- page long form documents
New feature: search within a manual
This is search within a manual A list of results
from a Manual with a nested list of results from the rest of GOV.UK
From this:
To this: From this:
None
Search pages use mustache (specifically: the ruby implementation of mustache
via the mustache gem)
From this:
To this: From this:
To this: then from this:
This is very uninteresting.
Using partials in this way is in the docs: from:
https://mustache.github.io/mustache.5.html#Partials
full PR is here: https://github.com/alphagov/frontend/pull/784
Time to deploy!
This should have been straightforward
None
-
-
We deploy to staging
We proceed to production
Slowly, shit hits the fan.
-
We rollback and investigate on staging
Breaking things isn’t bad if you have a good plan
to recover
Profiling
We found out the problem was this CPU spike
# Profile the code RubyProf.start ... [code to profile] ...
result = RubyProf.stop # Print a flat profile to text printer = RubyProf::FlatPrinter.new(result) printer.print(STDOUT) Measuring code with ruby-prof is easy
Profile of our code %self total self wait child calls
name 5.41 0.271 0.086 0.000 0.185 675 Mustache::Parser#scan_tags 3.90 0.062 0.062 0.000 0.000 36 BasicObject#instance_eval 3.34 1.031 0.053 0.000 0.978 1 ActionView::CompiledTemplates#_app 2.98 0.047 0.047 0.000 0.000 6 <Class::Dir>#[] 2.52 0.042 0.040 0.001 0.001 74 <Module::ActiveSupport::Multibyte> 2.36 0.040 0.037 0.000 0.002 1095 Mustache::Parser#regexp 2.08 0.051 0.033 0.000 0.018 46 <Module::Marshal>#load 1.99 0.032 0.032 0.000 0.000 6 Kernel#require 1.98 0.032 0.032 0.000 0.000 2181 String#to_s 1.67 0.506 0.027 0.000 0.480 1007 *Array#each 1.55 0.050 0.025 0.000 0.025 972 Psych::ScalarScanner#tokenize 1.48 0.024 0.024 0.000 0.000 168 <Class::File>#exist? 1.35 0.068 0.021 0.000 0.046 663 Mustache::Parser#position 1.31 0.066 0.021 0.000 0.045 675 Mustache::Parser#scan_text
Profile of our code %self total self wait child calls
name 5.41 0.271 0.086 0.000 0.185 675 Mustache::Parser#scan_tags 3.90 0.062 0.062 0.000 0.000 36 BasicObject#instance_eval 3.34 1.031 0.053 0.000 0.978 1 ActionView::CompiledTemplates#_app 2.98 0.047 0.047 0.000 0.000 6 <Class::Dir>#[] 2.52 0.042 0.040 0.001 0.001 74 <Module::ActiveSupport::Multibyte> 2.36 0.040 0.037 0.000 0.002 1095 Mustache::Parser#regexp 2.08 0.051 0.033 0.000 0.018 46 <Module::Marshal>#load 1.99 0.032 0.032 0.000 0.000 6 Kernel#require 1.98 0.032 0.032 0.000 0.000 2181 String#to_s 1.67 0.506 0.027 0.000 0.480 1007 *Array#each 1.55 0.050 0.025 0.000 0.025 972 Psych::ScalarScanner#tokenize 1.48 0.024 0.024 0.000 0.000 168 <Class::File>#exist? 1.35 0.068 0.021 0.000 0.046 663 Mustache::Parser#position 1.31 0.066 0.021 0.000 0.045 675 Mustache::Parser#scan_text Method name Time Calls
Profile of our code %self total self wait child calls
name 5.41 0.271 0.086 0.000 0.185 675 Mustache::Parser#scan_tags 3.90 0.062 0.062 0.000 0.000 36 BasicObject#instance_eval 3.34 1.031 0.053 0.000 0.978 1 ActionView::CompiledTemplates#_app 2.98 0.047 0.047 0.000 0.000 6 <Class::Dir>#[] 2.52 0.042 0.040 0.001 0.001 74 <Module::ActiveSupport::Multibyte> 2.36 0.040 0.037 0.000 0.002 1095 Mustache::Parser#regexp 2.08 0.051 0.033 0.000 0.018 46 <Module::Marshal>#load 1.99 0.032 0.032 0.000 0.000 6 Kernel#require 1.98 0.032 0.032 0.000 0.000 2181 String#to_s 1.67 0.506 0.027 0.000 0.480 1007 *Array#each 1.55 0.050 0.025 0.000 0.025 972 Psych::ScalarScanner#tokenize 1.48 0.024 0.024 0.000 0.000 168 <Class::File>#exist? 1.35 0.068 0.021 0.000 0.046 663 Mustache::Parser#position 1.31 0.066 0.021 0.000 0.045 675 Mustache::Parser#scan_text
Before and after self calls name self calls name 0.051
2 BasicObject#instance_eva 0.086 675 Mustache::Parser#scan_ta 0.042 6 <Class::Dir>#[] 0.062 36 BasicObject#instance_eva 0.014 110 Mustache::Parser#scan_ta 0.053 1 ActionView::CompiledTemp 0.014 337 *Array#each 0.047 6 <Class::Dir>#[] 0.004 145 Sprockets::Asset#depende 0.040 74 <Module::ActiveSupport:: 0.004 149 *Array#map 0.037 1095 Mustache::Parser#regexp 0.003 110 Mustache::Parser#scan_te 0.033 46 <Module::Marshal>#load 0.003 109 Mustache::Parser#positio 0.032 6 Kernel#require 0.003 110 Mustache::Parser#scan_un 0.032 2181 String#to_s 0.002 2 <Class::IO>#binread 0.027 1007 *Array#each 0.002 186 <Class::File>#exist? 0.025 972 Psych::ScalarScanner#tok 0.002 774 Kernel#is_a? 0.024 168 <Class::File>#exist? 0.002 186 Hike::Trail#stat 0.021 663 Mustache::Parser#positio 0.002 232 String#split 0.021 675 Mustache::Parser#scan_te
compiling templates is slow and partials are compiled before each
use
Recursion
Our results page has a list within a list
This is broken This is OK
- Iteration - Repetition - Recursion
- Iteration - Repetition - Recursion
Repetition
Repetition
This is fast but ghastly
- Iteration - Repetition ?? - Recursion
Recursion is a bit of a dick move
Recursion: Use responsibly
_results.mustache: {{#results}} {{#is_multiple_results}} <ol class= >{{>_results}}</ol> {{/is_multiple_results}} {{^is_multiple_results}} <li> result
code goes here </li> {{/is_multiple_results}} {{/results}}
_results.mustache: {{#results}} {{#is_multiple_results}} <ol class= >{{>_results}}</ol> /*Call self*/ {{/is_multiple_results}} {{^is_multiple_results}}
<li> result code goes here </li> {{/is_multiple_results}} {{/results}}
The problem with this is it’s non-obvious
- Iteration - Repetition ?? - Recursion ??
None
Reader, we chose the recursion.
Fixing the gem
Fixing the gem was the right thing to do
Fixing the gem prevents trouble in the future
Looking for prior art
The fix
We added a benchmark instead of a test
Writing the pull request https://github.com/mustache/mustache/pull/205
Explain the problem
Explain the change
Be proactive about objections
Keep nudging for progress
Over half our effort went on getting the change accepted
OK thanks bye
Links
Questions?