Hello! I'm David
I'm the Backend Team Lead at Chefs Plate.
You can find me on Twitter and GitHub at
@davidchchang
Slide 3
Slide 3 text
We're Hiring
(Intermediate/Senior) Front-End Developer
Data Engineer
Product Owner - Mobile
IT Administrator
Plus many more! Check out
https://www.chefsplate.com/careers
Slide 4
Slide 4 text
Optimizing PHP
Application
Performance
Slide 5
Slide 5 text
Performance Checklist
❄ Why performance matters
❄ Identifying performance issues
☆ Finding the culprit (with profiling)
❄ Tackling root causes:
☆ Slow database queries
☆ Caching non-performant results
☆ Expensive function calls and operations
❄ Improving testing and development processes
Slide 6
Slide 6 text
Why Performance Matters
Slide 7
Slide 7 text
Why Performance Matters
Slide 8
Slide 8 text
Speed Matters
❄ Better customer experiences
❄ Determines scalability
❄ Build and iterate faster
❄ Slower performance means more computing
resources needed = more expensive
Slide 9
Slide 9 text
Identifying Performance
Issues
First step: realizing you have a problem
Slide 10
Slide 10 text
“I'm not a real programmer. I
throw together things until it
works then I move on. The real
programmers will say: “Yeah, it
works but you're leaking
memory everywhere. Perhaps
we should fix that". I'll just
restart Apache every 10
requests.”
- Rasmus Lerdorf
Slide 11
Slide 11 text
80:20 rule (more like 95:5)
❄ Most of the times, 5-10% of your code base is
causing 95%+ of your performance issues
❄ Performance dips tend to be tied to code releases
❄ Sometimes massive traffic can reveal
infrastructure inadequacies
Slide 12
Slide 12 text
Finding the Culprit
Determine where the non-performant code is
coming from
"Knowing where to tap"
Slide 13
Slide 13 text
Knowing where to tap
Slide 14
Slide 14 text
Questions to Ask
❄ How long do your pages take to load?
❄ Is every request slow?
☆ What are your most frequently-used pages
and endpoints?
☆ Is there any code in common across those
pages?
❄ Did we release something recently that caused
the slowdown?
Slide 15
Slide 15 text
Cue: Application Performance Monitoring
❄ APMs like New Relic and Datadog help to reveal
underlying performance issues
❄ View high level performance metrics with enough
granularity of specific issues
❄ Can define thresholds for acceptable
performance and set up monitoring notifications
to get notified when application performance
degrades
Slide 16
Slide 16 text
No content
Slide 17
Slide 17 text
No content
Slide 18
Slide 18 text
Track execution times with chefsplate/php-profiler!
Slide 19
Slide 19 text
Example usage of chefsplate/php-profiler
Slide 20
Slide 20 text
When It's Not Your Code
❄ It could be an infrastructure issue
Slide 21
Slide 21 text
Slow DB Queries
Majority of taps
Slide 22
Slide 22 text
Creating Database Indexes
❄ Look-ups and queries perform dramatically faster
with the right indexes
❄ Look at what is frequently accessed in your
queries
❄ There are some caveats:
☆ Don't index everything
☆ Order of indexes can matter as well
Slide 23
Slide 23 text
Adding a database index usually improves query performance
Source: CodeProject, Benchmarking with indexed views
Slide 24
Slide 24 text
Offloading to the Database
❄ Offload as much of the application code back to
the database wherever possible
❄ Don't fetch objects into PHP and parse/populate
them when you can do it straight in the DB
❄ Utilize built-in aggregation and reporting
frameworks if available
Slide 25
Slide 25 text
20,000+
milliseconds!!
Slide 26
Slide 26 text
Offloading reporting logic from PHP to Mongo aggregations
Slide 27
Slide 27 text
Create-if-not-exists Queries
❄ Generating random unique numbers can be
expensive on larger tables/collections
☆ One solution: Retryable writes with unique
indexes will throw errors; catch exceptions
instead of querying for matches
Slide 28
Slide 28 text
Decoupling Database Tests
❄ Improve unit tests by decoupling and mocking
database calls
❄ Don't confuse integration or API tests with unit
tests
Slide 29
Slide 29 text
Caching
For frequently accessed content
Slide 30
Slide 30 text
Caching Guidelines
❄ Cache frequently accessed content
☆ Especially if the content doesn't change often
❄ Useful for "aggregated" content
☆ e.g. content that requires multiple DB queries
to assemble
❄ Always provide a fallback in case caching fails
❄ Make sure to wrap appropriate exception
handling logic around your cache calls
Slide 31
Slide 31 text
Using the Laravel cache + fallback to populate
Slide 32
Slide 32 text
Cache entire Laravel responses with Spatie's response cache package
Slide 33
Slide 33 text
❄ Great for caching entire responses
☆ Considers input parameters when caching
❄ Caching on a per-endpoint basis
❄ Not too much flexibility in busting caches
☆ All-or-nothing approach
Spatie Response Cache
Slide 34
Slide 34 text
No content
Slide 35
Slide 35 text
❄ Save calls to the database for commonly accessed
immutable data
❄ Only first call to a singleton entity should incur a
performance hit
❄ Retrieve further entities from memory directly
❄ Similar to APC cache
Singleton Entities
Slide 36
Slide 36 text
No content
Slide 37
Slide 37 text
Expensive Calls
Not all function calls are made equal
Slide 38
Slide 38 text
"Performance problems in
ecommerce are almost always
due to making too many calls
rather than the response time of
any given call."
— Kelly Goetsch, eCommerce in
the Cloud
Slide 39
Slide 39 text
Host-to-host communication is expensive
Source: Geeks Hangout, The OSI Model
Slide 40
Slide 40 text
Start Low Level
❄ Start from the bottom and then work your way
up
☆ Database
☆ Laravel MVC Framework
■ Data/persistence layer (with Eloquent
ORM/Doctrine ODM)
■ Domain layer (models)
■ Middleware
■ Controllers
☆ Templates and Views (Frontend)
Slide 41
Slide 41 text
❄ Improve performance by serving up assets closer
to your user base
❄ Reduce load on your web servers
❄ Automatic mobile image optimizations
❄ Leverage gains from GZIP compression and
HTTP/2 server push
Content Delivery Networks
Slide 42
Slide 42 text
Performance gains from using Cloudflare CDN
Slide 43
Slide 43 text
Some Things We've Tried
❄ Swapping out Hash::check bcrypt calls with
native SHA1 hashing via hash()
☆ Hashing operation went from 192ms to 4ms
❄ Enabling APCu caching for Doctrine metadata:
☆ Saved ~60ms per request
Slide 44
Slide 44 text
❄ Replacing splat type-hints (Class … $val)
syntax with plain array setting
☆ This ended up being 3X faster
❄ Removing magic setters and getters
☆ ~0.0009ms - not worth it!
Some Things We've Tried
Slide 45
Slide 45 text
Testing & Dev Processes
Other areas for optimizations
Slide 46
Slide 46 text
"Only conducting performance
testing at the conclusion of
system or functional testing is
like conducting a diagnostic
blood test on a patient who is
already dead."
— Scott Barber
Slide 47
Slide 47 text
Performance Testing
❄ Performance testing would be ideal to integrate
into your CI
❄ Log ms before and after
❄ Maintain a specific level of performance
Slide 48
Slide 48 text
Speeding Up your Testing
❄ Extract out your database calls
❄ Mock expensive or difficult to reproduce calls
(esp. third party)
❄ Run tests in parallel
❄ Build in performance into your development
considerations
☆ Overall speed up of your CI process
Slide 49
Slide 49 text
Improve testing performance with Paratest
Slide 50
Slide 50 text
Save your life with hirak/prestissimo
Slide 51
Slide 51 text
Save your Life with prestissimo
❄ Makes composer installs at least ten times faster
❄ Just type:
composer global require hirak/prestissimo
☆ There's no turning back
❄ Our installs went from 15+ minutes to 17 seconds
❄ TODO: Buy this guy a beer
Slide 52
Slide 52 text
There's no turning back once installed
Slide 53
Slide 53 text
Recap
Use APMs and
profilers to help you
identify performance
issues
Apply
indexing and
caching
techniques &
avoid
expensive
calls
Profit!
(even more
than usual!)
Slide 54
Slide 54 text
Thanks!
Questions?
Slide 55
Slide 55 text
Happy Holidays
and a Happy New Year!
From your @LaravelTO friends
Slide 56
Slide 56 text
❄ Feb 27, 2018: Presentation TBA
❄ Apr 25, 2018: Video Chat with Taylor Otwell
❄ Jun 20, 2018: Workshop with Adam Wathan
Upcoming Meetups