Improving the performance of a laravel application

Doing development in laravel is a quick and pleasurable experience. Regardless of that, by the time you get to deploy the application, you might realise the application doesn't perform well under real circumstances.

What I'd like you to realise is that there's no silver bullet. Complete optimization comes through hard work, nitpicking every tiny aspect of the application that might slow it down. But use these little tricks I wrote here, and you should be okay.

Config caching

The laravel config spreads across dozens of files, and `including` every one of them for each request is a costly process. To combine all of your config files into one, use:
    php artisan config:cache
    
Keep in mind that any changes to the config will not have any effect once you cache it it. To refresh the config cache, run the above command again. In case you want to completely get rid of the config cache, run
php artisan config:clear

Routes caching

Routing is also an expensive task in laravel. To cache the routes.php file run the below command:
php artisan route:cache

Mind that it doesn't work with closures. In case you're using closures this is a great chance to move them into a controller, as the artisan command will throw an exception when trying to compile routes that are bound to closures instead of a proper controller methods.
In the same as the config cache, any changes to routes.php will not have any effect anymore. To refresh the cache, run the above command everytime you do a change to the routes file. To completely get rid of the route cache, run the below command:

php artisan route:clear

Classmap optimization

It's not uncommon for a medium-sized project to be spread across hundreds of PHP files. As good coding behaviours dictate us, everything has its own file. This, of course, does not come without drawbacks. Laravel has to include dozens of different files for each request, which is a costly thing to do.

Hence, a good optimization method is declaring which files are used for every request (this is, for example, all your service providers, middlewares and a few more) and combining them in only one file, which will be afterwards loaded for each request. This not different from combining all your javascript files into one, so the browser will have to make fewer requests to the server.

The additional compiles files (again: service providers, middlewares and so on) should be declared by you in config/compile.php, in the files key. Once you put there everything essential for every request made to your app, concatenate them in one file with:

php artisan optimize --force

Optimizing the composer autoload

This one is not only for laravel, but for any application that's making use of composer.

I'll explain first how the PSR-4 autoload works, and then I'll show you what command you should run to optimize it. If you're not interested in knowing how composer works, I recommend you jumping directly to the console command.

When you ask compsoser for the App\Controllers\AuthController class, it first searches for a direct association in the classmap. The classmap is an array with 1-to-1 associations of classes and files. Since, of course, you did not manually add the Login class and its associated file to the classmap, composer will move on and search in the namespaces.
Because App is a PSR-4 namespace, which comes by default with Laravel and it's associated to the app/ folder, composer will try converting the PSR-4 class name to a filename with basic string manipulation procedures. In the end, it guesses that App\Controllers\AuthController must be located in a AuthController.php file, which is in a Controllers/ folder that should luckily be in the namespace folder, which is app/.

All this hard work only to get that the App\Controllers\AuthController class exists in the app/Controllers/AuthController.php file. In order to have composer scanning your entire application and create direct 1-to-1 associations of classes and files, run the following command:

composer dumpautoload -o

Keep in mind that if you already ran php artisan optimize --force, you don't have to run this one anymore. Since the optimize command already tells composer to create an optimized autoload.

JIT Compiler

PHP is not understood natively by computers. You can't compile it to bytecode and have machines running it. It must be done through a middleman, like the zend engine, that inteprets your php files and executes C routines accordingly. As you might guess, this is slow. Everytime your server runs a php file, it has to convert it to tokens - done by the AST parser and intepret it. It unfortunately has to compile it every single time, even though it gets the same result.

For your application to act fast, you need a compile it once, run it every time method, and this is what a JIT compiler is about.

The recommended JIT compiler for Laravel is HHVM, created and used extensively by Facebook. It's also used by Wikipedia, Etsy and thousands of others.

Choose a faster cache and session driver

Saving sessions in files is a quick and decent enough method that's been used since the beggining of PHP's time. But if you're looking for performance, the filesystem is one of the things you should watch for, as it's slow. A better method is storing the cache and session entries in RAM, since it provides a way faster access to the data. Luckily for you, laravel supports a few caching and session drivers that store data in RAM.

My recommendation for the cache and the session driver is memcached, but you can choose whatever you like, as long as it works with RAM memory.

To change the session driver, check out the "driver" key from the following file:

app/config/session.php
To change the cache driver, check out the "driver" key from the following file.
app/config/cache.php

Don't underestimate the speed you gain by optimizing your queries

As you saw, most of the optimization methods make use in one way of another of caching. But when it comes to database queries optimization, you should not rely on caching. Caching should be the last resort for optimizing a query.

Cache the queries results

Because mysql is not gonna do this for you, not as good as you can do it by yourself. Definitely you're not going to cache every single query result from your application, look at the big picture! What queries are ran most frequently in your application, do they really have to be executed that often? Wouldn't running the query each 15 minutes and serving the result to every user achieve the same result?

The great thing that comes with removing the remember method from the query builder. (which was a great feature, but not great enough - people seemed to overestimate its power) is that you'll use more the Cache::remember function, as follows.

$posts = Cache::remember('index.posts', 30, function()
{
    return Post::with('comments', 'tags', 'author', 'seo')->whereHidden(0)->get();
});
Similar articles
Laravel translations - 2 years ago
It's pretty common to need the validation errors in a different language than English, which is the default for Laravel.  If you don't already know this p...
Moving to the UK | Checklist - 1 year ago
In less than two weeks I'm going to call Chester "home". The reasons behind my relocation to the UK were many, but the most pronounced one was that I felt like t...
Comments