Contents
-
PHP
PHP7
OPcache
JIT
-
Laravel
Config Cache
Route Cache
Autoloader
-
Database
Cache
Eager Loading
Chunk
PHP
PHP7
PHP7相比PHP5.6有至少1倍以上的性能提升
让PHP7达到最高性能的几个Tips
PHP的性能演进(从PHP5.0到PHP7.1的性能全评测)
The Definitive PHP 5.6, 7.0, 7.1, 7.2 & 7.3 Benchmarks (2019)
PHP7更多特性可以参考从PHP 5.6.x 移植到 PHP 7.0.x - 新特性
OPcache
OPcache将PHP代码预编译生成的脚本文件Opcode缓存在共享内存中供以后反复使用 从而避免了从磁盘读取代码再次编译的时间消耗
- Config OPcache
php -m | grep "Zend OPcache"
# Zend OPcache
sudo vim /etc/php/7.1/cli/php.ini
# opcache.validate_timestamps=0
vim cache.php
php -S 0.0.0.0:8088
curl http://192.168.56.201:8088/cache.php
# 此时修改cache.php 修改不会生效
- PHP info
vim info.php
curl http://192.168.56.201:8088/info.php
- OpCacheGUI
git clone https://github.com/PeeHaa/OpCacheGUI.git && cd OpCacheGUI
cp config.sample.php config.php
sudo vim /etc/nginx/sites-enabled/opcache.conf
server {
listen 80;
server_name 192.168.56.201.xip.io;
root /home/op/OpCacheGUI/public;
try_files $uri $uri/ /index.php;
location ~* \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php7.1-fpm.sock;
}
}
sudo nginx -t && sudo nginx -s reload
curl http://192.168.56.201.xip.io/index.php
Opcode是啥以及如何使用好Opcache
PHP7开启Opcode打造强悍性能
让子弹飞~利用 OPcache 扩展提升 PHP7 性能 | Laravel 篇
JIT
JIT(Just-In-Time) Compiler 即时编译器
默认
PHP源代码 => 经过zend编译=> OPcode字节码 => PHP解释器 => 机器码
启用OPcache
PHP源代码 => 查找OPcache 没有则经过zend编译 => OPcode字节码 => PHP解释器 => 机器码
启用JIT
PHP源代码 =>编译 => 机器码
PHP 霸主地位被动摇,JIT 是穷途末路后的绝地反击?
HHVM 是如何提升 PHP 性能的?
php 的 opcache 和最近的 php jit 有什么区别?
JIT理论性能优于OPcache 因为JIT缓存机器码而OPcache缓存Opcode字节码
Laravel
Config Cache
php artisan config:cache
# bootstrap/cache/config.php
php artisan config:clear
# vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/LoadEnvironmentVariables.php
class LoadEnvironmentVariables
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
if ($app->configurationIsCached()) {
return;
}
$this->checkForSpecificEnvironmentFile($app);
try {
(new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
} catch (InvalidPathException $e) {
//
}
}
}
- Configuration Caching
If you execute the config:cache command during your deployment process, you should be sure that you are only calling the env function from within your configuration files. Once the configuration has been cached, the .env file will not be loaded and all calls to the env function will return null.
Route Cache
In some cases, your route registration may even be up to 100x faster.
php artisan route:cache
# bootstrap/cache/routes.php
php artisan route:clear
# vendor/laravel/framework/src/Illuminate/Foundation/Support/Providers/RouteServiceProvider.php
class RouteServiceProvider extends ServiceProvider
{
/**
* The controller namespace for the application.
*
* @var string|null
*/
protected $namespace;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->setRootControllerNamespace();
if ($this->app->routesAreCached()) {
$this->loadCachedRoutes();
} else {
$this->loadRoutes();
$this->app->booted(function () {
$this->app['router']->getRoutes()->refreshNameLookups();
$this->app['router']->getRoutes()->refreshActionLookups();
});
}
}
}
# vendor/laravel/framework/src/Illuminate/Foundation/Application.php
class Application extends Container implements ApplicationContract, HttpKernelInterface
{
/**
* Determine if the application routes are cached.
*
* @return bool
*/
public function routesAreCached()
{
return $this['files']->exists($this->getCachedRoutesPath());
}
/**
* Get the path to the routes cache file.
*
* @return string
*/
public function getCachedRoutesPath()
{
return $this->bootstrapPath().'/cache/routes.php';
}
}
- Route Caching
Closure based routes cannot be cached. To use route caching, you must convert any Closure routes to controller classes.
Autoloader
composer install
# 包含composer dumpautoload -o
# bootstrap/cache/packages.php
# bootstrap/cache/services.php
# composer.json
{
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover"
]
},
}
# vendor/laravel/framework/src/Illuminate/Foundation/ComposerScripts.php
class ComposerScripts
{
/**
* Handle the post-autoload-dump Composer event.
*
* @param \Composer\Script\Event $event
* @return void
*/
public static function postAutoloadDump(Event $event)
{
require_once $event->getComposer()->getConfig()->get('vendor-dir').'/autoload.php';
static::clearCompiled();
}
}
# vendor/laravel/framework/src/Illuminate/Foundation/Console/PackageDiscoverCommand.php
class PackageDiscoverCommand extends Command
{
/**
* The console command signature.
*
* @var string
*/
protected $signature = 'package:discover';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Rebuild the cached package manifest';
/**
* Execute the console command.
*
* @param \Illuminate\Foundation\PackageManifest $manifest
* @return void
*/
public function handle(PackageManifest $manifest)
{
$manifest->build();
foreach (array_keys($manifest->manifest) as $package) {
$this->line("Discovered Package: {$package}");
}
$this->info('Package manifest generated successfully.');
}
}
# vendor/laravel/framework/src/Illuminate/Foundation/Application.php
class Application extends Container implements ApplicationContract, HttpKernelInterface
{
/**
* Create a new Illuminate application instance.
*
* @param string|null $basePath
* @return void
*/
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
/**
* Register the basic bindings into the container.
*
* @return void
*/
protected function registerBaseBindings()
{
static::setInstance($this);
$this->instance('app', $this);
$this->instance(Container::class, $this);
$this->instance(PackageManifest::class, new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
));
}
/**
* Register all of the configured providers.
*
* @return void
*/
public function registerConfiguredProviders()
{
$providers = Collection::make($this->config['app.providers'])
->partition(function ($provider) {
return Str::startsWith($provider, 'Illuminate\\');
});
$providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
->load($providers->collapse()->toArray());
}
/**
* Get the path to the cached services.php file.
*
* @return string
*/
public function getCachedServicesPath()
{
return $this->bootstrapPath().'/cache/services.php';
}
/**
* Get the path to the cached packages.php file.
*
* @return string
*/
public function getCachedPackagesPath()
{
return $this->bootstrapPath().'/cache/packages.php';
}
}
- 深入了解 Laravel 5.5 Package Auto Discovery
Database
Cache
- 使用Redis存储Session
# config/database.php
return [
'redis' => [
'client' => 'predis',
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
],
];
# config/session.php
return [
'driver' => env('SESSION_DRIVER', 'redis'),
'connection' => 'default',
];
# .env
SESSION_DRIVER=redis
- 使用Cache存储Data
$value = Cache::remember('users', $minutes, function () {
return DB::table('users')->get();
});
Eager Loading
- Lazy Loading
$books = App\Book::all();
foreach ($books as $book) {
echo $book->author->name;
}
Lazy Loading可能引起N+1性能问题 详见记一次慢查询问题的解决过程 / 预加载
- Eager Loading
$books = App\Book::with('author')->get();
foreach ($books as $book) {
echo $book->author->name;
}
Eager Loading单次查询会获取更多数据 导致内存占用高
Chunk
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
// code
}
});
参考
Laravel 性能问题始终难以释怀,求指点
十个 Laravel 5 程序优化技巧
12 Tips for Laravel Performance Optimization
appstract/laravel-opcache
深入浅出 JIT 编译器