Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
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
570
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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
Other Decks in Programming
See All in Programming
S3 TablesのIcebergデータに、AIエージェントからアクセスしてみた / 20261016developersio2026joetsu-accessing-iceberg-in-s3tables-via-agent
kasacchiful
PRO
0
230
APNsからLive Activityを開始する話
yumnumm
0
180
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
390
オーケストレーション記述言語 Katari
yukikurage
0
200
3回つくってわかった、 定期実行ジョブ基盤の本質情報
takai
4
3.4k
Performance Optimization With LLMs
xleitix
0
220
contenteditable と日本語入力に向き合う
colorful12
0
230
気持ちぃ〜角丸 Squircle
ryu1013
0
360
Seven New Principles of Software Engineering
yegor256
0
370
個人開発者よ、Honoを使え(おじさんver)
tanahiro2010
2
290
そのデザイントークン、Flutterでも同じ見た目になりますか?
nakamizu
0
110
最近”作らせてる” ローカルAIアプリの話
akit37
0
510
Featured
See All Featured
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
33
5.4k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
2
2.2k
Designing for humans not robots
tammielis
254
26k
[SF Ruby Conf 2025] Rails X
palkan
3
1.5k
Fashionably flexible responsive web design (full day workshop)
malarkey
409
67k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.5k
Agile that works and the tools we love
rasmusluckow
331
22k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
740
Fantastic passwords and where to find them - at NoRuKo
philnash
53
4k
Building AI with AI
inesmontani
PRO
1
1.3k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
280
The Curse of the Amulet
leimatthew05
3
16k
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?