Performance Profiling in Laravel: Finding and Fixing Bottlenecks
Measure Before Optimizing
Performance optimization without measurement is guesswork. Before changing anything, establish baselines and identify actual bottlenecks. The slowest part of your application might not be where you think it is.
Laravel Debugbar
For development, Debugbar provides immediate visibility:
composer require barryvdh/laravel-debugbar --dev
Debugbar shows queries, memory usage, route information, and timing breakdowns for every request.
Laravel Telescope
Telescope provides deeper insights for debugging:
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
Use it to inspect requests, exceptions, database queries, queued jobs, scheduled tasks, and more.
Database Query Analysis
// Enable query logging
DB::enableQueryLog();
// Your code here
// Review queries
dd(DB::getQueryLog());
// Or listen to queries
DB::listen(function ($query) {
if ($query->time > 100) { // > 100ms
Log::warning('Slow query', [
'sql' => $query->sql,
'time' => $query->time,
]);
}
});
Identifying N+1 Problems
// In AppServiceProvider
public function boot(): void
{
Model::preventLazyLoading(!app()->isProduction());
}
// This throws an exception when lazy loading occurs in non-production
Profiling with Xdebug and Blackfire
For detailed profiling, use Xdebug's profiler or Blackfire.io:
# Xdebug configuration
xdebug.mode = profile
xdebug.output_dir = /tmp/xdebug
Analyze the cachegrind files with tools like KCachegrind or QCachegrind.
Memory Profiling
// Track memory usage
$start = memory_get_usage();
// Your code
$end = memory_get_usage();
Log::info('Memory used: ' . ($end - $start) / 1024 / 1024 . 'MB');
Production Monitoring
Use APM tools in production:
- New Relic
- Datadog
- Scout APM
- Laravel Pulse
Common Optimizations
// Route caching
php artisan route:cache
// Config caching
php artisan config:cache
// View caching
php artisan view:cache
// Optimize autoloader
composer install --optimize-autoloader --no-dev
Query Optimization
// Use select to limit columns
User::select(['id', 'name', 'email'])->get();
// Use cursor for memory efficiency
User::cursor()->each(function ($user) {
// Process one at a time
});
// Add database indexes
Schema::table('orders', function ($table) {
$table->index(['user_id', 'created_at']);
});
Caching Strategies
// Cache expensive queries
$popularProducts = Cache::remember('popular_products', 3600, function () {
return Product::withCount('orders')
->orderByDesc('orders_count')
->limit(10)
->get();
});
Conclusion
Profile first, optimize second. Use the right tools for the job, and always measure the impact of your optimizations. Small improvements compound—a 10% improvement in ten places is a 2.6x overall improvement.
Related Articles
Need Help With Your Project?
I respond to all inquiries within 24 hours. Let's discuss how I can help build your production-ready system.
Get In Touch