We aim to dispel the notion that large PHP applications tend to be sluggish, resource-intensive and slow compared to what the likes of Python, Erlang or even Node can do. The issue is not with optimising PHP internals - it's the lack of proper introspection tools and getting them into our every day workflow that counts! In this workshop we will talk about our struggles with whipping PHP Applications into shape, as well as work together on some of the more interesting examples of CPU or IO drain.
8. 1. Edit the /Docker/.env file by inserting your own BlackFire credentials and changing the ports if needed.
2. Enter the project’s Docker folder and execute ./deploy.sh.
3. Visit http://localhost:80/. That’s it.
27. 1. Profile key pages
2. Start from the slowest ones
3. Compare and analyze profiles to spot differences and bottlenecks
4. Always look for the biggest (solvable) bottleneck
5. Not all bottlenecks should be solved, sometimes they are the price of business
28. The creature in the picture is a “Dwarf in the flask” from
Fullmetal Alchemist anime.
It was a successful experiment which broke its own flask
and eventually nearly managed to kill everyone.
The author did not plan on such success, so they did not
take a great amount of precautions.
Sometimes, you just grew out of your flask. Your failed
performance can be compensated for via creating a good
flexible app && server architecture.
29. The solution is not always the code, it lies more often in the
architecture of the app. Keep it as simple as possible, but
prepare for the future.
Splitting responsibilities, granulating your application into smaller
ones, DB sharding or caching, CDN, continuous integration.
But this is a topic for a whole set of other workshops...
30.
31. When faced with a really heavy query, there is usually not much you can do.
Sometimes you can do better indexing, optimize joins, even do some caching.
40k Client entities in total
Sort and extract top 10. Based on a complex set of requirements.
Symfony 3.4 | MongoDB 3.3 | Doctrine ODM
Query Took ~6.2 seconds to complete.
On each non-cached load of this page.
32. The business logic is what it is, you can’t make it simpler because it suits you.
What you would usually do is start optimizing the query. Indexing, making joins when
possible etc.
But in this case all of that was already done by the previous team…
So, it seems that no code could be improved.
33. Not true. Once you have a bird’s-eye view of the code execution you start to notice the real
problems.
1. By some weird and not-easy-observable edge case, the query is run twice.
2. The real bottleneck is not DB, it is ODM.
Based on this, first we saved 3.1s by handling the edge case.
Then we created a new reader which simply uses direct DB queries for this particular query.
In the end, we use Symfony Cache mechanism to only run this once per day.
34.
35. Sometimes, you simply don’t have the real need for using Doctrine document Hydration.
Yes, i said it.
Instead you can instead use Array or Scalar fetch methods.
36.
37. What is little known to developers who have never worked dealt withSQL directly is the
ability to prepare changes and trigger a DB transaction at a specific time.
In doctrine it’s called a flush().
All the changes that you make on entities are only saved to the database once you call the
flush method.
This is a double edged sword...
40. Large transactions can also be slooow.
You need to find a sweetspot if dealing with large
datasets.
With MongoDB we usually use ~400 entities per
transaction, except in cases where there are a lot
of changes.
41. Sometimes you don’t even have to loop. Try using multi updates when updating the
whole set of documents in the same way.
42.
43. ● Always evaluate query speed when not sure.
● Use Cursors whenever possible.
● Watch out for N+1 Problems
● DB Calls within a loop - use prepared statements.
● ***Learn to properly use Indexes and Cardinality***
● Enable slow query log and make sure none of your queries do a full table scan.
44. If all else is done but you still have a problem with the volume of work (user visits, queries) it
might be time to start researching scaling techniques. (remember our Dwarf in the flask?)
Usually in these applications the same bottlenecks remain present.
Research database sharding, replication and DB caching layer.
47. Long running scripts often deal with some sort of watcher jobs or migrations.
For example, here we got this 160.000 users, take each one and match them to their
respective data from another system via a backchannel call.
Take a user from DB, Call API and match, Save changes. Repeat x140k.
48. ● In these cases optimizing cache is useless, and workload is often huge.
● PHP has a composer vendor libraries problem with RAM leaks.
● Script works for hours, racks up maximum ram and then stalls slower and slower when
GC kicks in.
49.
50. Always practice to use Dependency Injection instead of (ab)using Container directly.
Apart from being a prettier and more maintainable code style, this allows Symfony to
not-recompile parts of the container during the request.
51.
52. One of our sites used IPS Community Forums and was running PHP 7.0
The vendor fully supports PHP up to 5.4 and 7.4.
One of the plugins installed, the author used mb_strripos which only works good on 7.3
...
53.
54. Sometimes a cache can be slow.
By default, most built-in caching mechanisms use Filesystem storage.
If you hit this bump, try with Redis, Varnish, or memcache.
Combine technologies when you see an opportunity.
55. ● When instantiating objects within a big loop, use a factory which clones a prototype of
the object instead of new MyObject(); (66% faster).
● If you are working with objects of large cardinality, use named classes instead of
arrays. Use stdclass never.
● Avoid these slow built-in PHP functions.
https://github.com/dseguy/clearPHP/blob/master/rules/avoid-those-slow-functions.md
● Use associative arrays basically never.
https://steemit.com/php/@crell/php-use-associative-arrays-basically-never
56. Don’t optimize before
knowing what is slowing down
your application
“For premature optimization is
the root of all evil.”