
Performance and Speed Optimizations of WordPress [pdf] - lamosty
https://lamosty.com/app/uploads/2015/02/bachelors-final.pdf
======
jacques_chester
I've run wordpress sites for more than a decade. I've never had an experience
with opcode caching that wasn't unhappy.

I've used all of them. They all crash like a demolition derby in a brewery.

At one point I put Wordpress into a profiler and found that a only a few
percent of total time was spent compiling PHP; most of the time spent was in
waiting for MySQL and concatenating strings. So I just gave up on opcode
caching.

The biggest improvements I got were:

* Whole page caching. In my case, WP-supercache spitting out gzip'd pages, with Nginx configured to find and serve those gzip'd pages.

* MySQL query caching.

* Moving MySQL into another server. This was a decent win when I was on spinning rust.

* SSDs. MySQL is a major chokepoint for Wordpress, because until recently its idea of a query plan was "let's join half the workloads on disk in gigantic join tables".

* Killing bad plugins. Even the "recent comments" plugin that ships with mainline is horrendous.

~~~
lamosty
You got some good points here though I don't know why opcode caching didn't
work for you. Nowadays, it's just a matter of putting some configuration lines
into PHP-FPM config and it works like a charm (at least for me, you might have
a special site, idk).

Yes, compiling PHP shouldn't take that much (even though typical WordPress
site consists of thousands of PHP files). But as you can see from some of the
charts from my work, it can potentially double the amount of concurrent
requests the server is able to respond to.

Whole page caching (I call it page cache) is the biggest improvement one can
make. However, instead of using WP plugins, in my work I configured Nginx to
cache the output of PHP interpreter (FastCGI). The advantages are that you can
use Nginx's location directives to bypass the cache early (e.g. logged in
users, shopping cart, etc) and that Nginx stores the pages in RAM in a tree-
like structure with pretty fast access times. In a heavy WP site (plenty of
large plugins, etc), you can go from 200 concurrent requests at 8 seconds to
1000 concurrent requests at 2 seconds, which is a pretty huge improvement.

MySQL query caching should definitely help, though in my testing it did not.
Maybe I need a real benchmark with a real 100 people requesting different
subpages. Then the DB (MariaDB in my case) might become a bottleneck.

Yeah, SSDs are superb-useful, in any cases. I made all the tests on a non-SSD
server. At least the WordPress DB structure is quite flexible, though, as you
mentioned, it now suffers from the gigantic joins.

Bad plugins and themes are the root of all the performance problems. But
sometimes it is easier to implement caching and replacing Apache with Nginx on
the server side than replacing plugins that you really need. That's the main
point of the thesis: Time of an experienced developer fixing plugins and
themes is more expensive than optimizing the server. But yeah, if you are a
developer, learning to write clean and fast plugins is crucial.

~~~
jacques_chester
It wasn't that configuring opcode caches is complicated or difficult. It's
that they locked up every few days, no matter which one I used or how I
configured them.

At the point where I wrote a cron script to kill PHP every 24 hours, I
realised the extra few percent weren't worth it.

Maybe it's improved, but: four times bitten, twice shy.

> _Nginx stores the pages in RAM in a tree-like structure with pretty fast
> access times._

On disk caching delegates this to the OS, which is pretty good at it.

Badly behaved plugins and themes are not a solvable problem on Wordpress,
because it's essentially a cooperative multitasking environment. One bad actor
can hog all the resources and there's no way to constrain it.

It seems as though very few plugin authors know what O-notation means (so many
nested loops), what EXPLAIN QUERY is or that tinkering and and firing up a
copy on your laptop isn't really testing.

~~~
lamosty
> Maybe it's improved, but: four times bitten, twice shy.

I see, happens all the time.

> On disk caching delegates this to the OS, which is pretty good at it.

Yeah, it's just a matter of preference and convenience.

> It seems as though very few plugin authors know what O-notation means (so
> many nested loops), what EXPLAIN QUERY is or that tinkering and and firing
> up a copy on your laptop isn't really testing.

This is the real issue. I know a very few WordPress developers who have a
formal technical education. While you can learn it yourself, school forces you
to learn this in a great detail. In a near future, I will hopefully write on
these topics, though, I need to study on this a bit more first.

~~~
jacques_chester
Well in the old days, the operating system was there to help you. If plugins
were standalone processes, they could be resource-constrained. But they all
have to run in-process because of the LAMP architecture.

Hence -- cooperative multitasking.

I've been complaining about it for years. Eg:
[http://clubtroppo.com.au/2008/07/10/shared-hosting-is-
doomed...](http://clubtroppo.com.au/2008/07/10/shared-hosting-is-doomed-and-i-
have-the-graphs-to-prove-it/)

~~~
lamosty
Thanks for the article, it's insightful and I & history agree completely with
you. Nowadays, people are using prebuilt VPS images or automation scripts to
provision their servers and shared hostings are becoming more of a VPS
hostings with a good UI around these tools.

I see that you are a WordPress veteran, nice to hear your opinions :)

------
justindocanto
Lead Developer of TheDirty.com here. Nice work. We're built on WordPress,
running on a single machine, using Nginx + PHP-FPM, and currently serving
~20,000,000 pageviews a month.

Before I took over development we were running on apache w/ vanilla PHP and
CPU's would be > 75% on a regular basis. The site frequently ran into the
Apache 10k issue you mentioned in your paper as well. After getting the new
server setup & optimizing all our MySQL queries, it's abnormal for all of our
CPUs to even be doing anything. Out of all our CPUs, we might have 5 < 10%
currently.

One thing I've gathered from this project is technique is a huge part of
optimizing WordPress. 1 resource hog at this scale is greatly amplified and
can bring everything crashing down. I remember the first time I coded a simple
'popular posts' widget and how hard that hit when you have 150,000 posts to
run through and 300,000+ pages generating on a regular basis throughout the
day. We had to scrape it the same week it went up.

One big example on how thinking about your queries is huge (let's ignore page
caching for a moment) is if you have a menu that queries all your categories
(We have 1,000+) in the menu on every page load, and then run that query every
time you generate a page, that would be ~650,000 times a day that menu query
is run and the menu is generated. When you think about running one query on a
table with 1,000+ rows, it sounds like nothing. But when you have to do it
650,000+ times a day... It adds up.

By switching our menu to be generated once every 5 minutes, having it cached
in html each time, and by having that menu loaded onto every page via
javascript instead; we only have to query & generate the menu 288 times a day.
Apply this "single query/generation + load in js technique" to things like
widgets within WordPress (all our related posts areas are loaded like this),
it has made our resource usage less than 1% of what it used to be.

Although people get really excited about things like widgets & cool custom
plugins... It's good to remember, if you can code, that sometimes hardcoding
and/or coming up with your own technique will save you a ton of cpu/ram usage
if you truly want to build for scale. Every single query counts. Nginx is
crucial > 10k connections & php-fpm is a great partner for nginx as well. I'm
going to print out your thesis and run through it a few times. Nice work my
friend.

\----

Also, idk why somebody said APC is dead. One of the most, if not the most,
widely used caching plugins, W3TC, still supports APC caching and a lot of
talks about WordPress optimization use APC. Personally, I find other ways to
be better, but that's another topic.

~~~
pierre_massat
I think people are saying APC is dead because another opcode cache was
included in PHP 5.5 and APC has not seen a release since 2012.

[https://en.wikipedia.org/wiki/List_of_PHP_accelerators#Alter...](https://en.wikipedia.org/wiki/List_of_PHP_accelerators#Alternative_PHP_Cache_.28APC.29)
[http://pecl.php.net/package/apc](http://pecl.php.net/package/apc)

~~~
erik_p
There is still a need for a ucache (in addition to built in opcode cache)...
although a lot of high traffic WP sites are turning to redis over memcache for
this.

------
ne01
WordPress is slow by design! I have managed to create a blogging engine (in
PHP) that it only takes 2ms to create the page dynamically (without caching)
from the database + ~20ms for Nginx to serve it.

One might argue that my engine does not have as much features... and you are
right.

Incase you are interested: SunSed.com

~~~
lamosty
WordPress is rather big, having more than 200K function calls on a typical
request to a blog post. However, when using the default Twenty Fifteen theme,
the performance is not that bad, as you can see from
[http://ldr.io/1cbbomp](http://ldr.io/1cbbomp) (VPS server with 3CPUs and 4GB
of RAM).

I've just checked your blogging engine and it looks very minimalistic. I like
it.

~~~
ne01
Thank you! Yes you are right! WordPress is huge! but not everyone need all the
functionality... let's just say 10% of current WordPress users just need a
simple blogging platform without all the widgets... then I think 1K function
calls should be enough!

Anyway, I love WordPress and do not compare my hubby project with 10 years of
1000+ developers time!

------
imaginenore
Looks outdated. APC cache is no longer, for instance.

~~~
lamosty
Author here. The figure depicting the workings of APC cache was just an
example of how opcode caching in PHP works. The thesis was published just
today.

~~~
moebis
Bratislava! Partizanske here. Just moved here from the United States (NJ). I'm
going to give this a good read. Thanks!

~~~
lamosty
Howdy! If you want to replicate the load testing on your server(s), use my
Ansible Playbooks: [https://github.com/lamosty/wordpress-
ansible](https://github.com/lamosty/wordpress-ansible) (part of the work).

