📄Performance Optimization Guide
SiroPHP is engineered for maximum performance. This guide covers benchmarking, optimization techniques, and best practices to achieve sub-millisecond response t
Overview#
SiroPHP is engineered for maximum performance. This guide covers benchmarking, optimization techniques, and best practices to achieve sub-millisecond response times.
📊 Benchmark Results#
Cold Boot Performance#
Benchmarked on Linux 6.8, PHP 8.2, OPcache enabled:
App boot + dispatch: 0.87ms (Linux, OPcache warm)
App boot + dispatch: 1.10ms (Linux, cold)
App boot + dispatch: 7.85ms (Windows, no OPcache)
Memory overhead: +16KB (all platforms)
Windows cold boot is slower due to filesystem I/O (directory creation, config file scanning). Production should always use Linux + OPcache for optimal performance.
Warm Request Throughput#
GET / (root): 522,459 ops/s
GET /nonexistent: 831,214 ops/s
POST /auth/login: 161 ops/s (with middleware)
POST /auth/register: 147 ops/s (with validation)
Router Performance#
Static route match: 514,954 ops/s
Param route match: 290,022 ops/s
Multi-param route: 243,064 ops/s
Grouped route: 893,736 ops/s ⭐
404 miss: 688,720 ops/s
Summary#
Average throughput: 398,563 ops/s
Best throughput: 893,736 ops/s
Fastest request: ~0.00ms (sub-millisecond!)
Memory per request: +0KB (zero overhead!)
🔍 Run Benchmarks#
Built-in Benchmark Command#
# Run comprehensive benchmarks
php siro benchmark
# Output includes:
# - Cold boot time
# - Warm request throughput
# - Router performance
# - Memory usage
# - Comparison with other frameworks
Custom Benchmarks#
<?php
// tests/benchmark_custom.php
require_once __DIR__ . '/../vendor/autoload.php';
use Siro\Core\App;
use Siro\Core\Route;
$app = new App();
Route::get('/test', function() {
return ['message' => 'Hello'];
});
$iterations = 10000;
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$_SERVER['REQUEST_URI'] = '/test';
$_SERVER['REQUEST_METHOD'] = 'GET';
$app->run();
}
$end = microtime(true);
$time = ($end - $start) * 1000; // ms
$opsPerSec = ($iterations / ($end - $start));
echo "Time: {$time}ms\n";
echo "Ops/sec: " . number_format($opsPerSec) . "\n";
echo "Avg per request: " . number_format($time / $iterations, 4) . "ms\n";
⚡ Optimization Techniques#
1. Config Caching#
Cache configuration for production:
php siro config:cache
Benefits:
- Eliminates
.envfile parsing on every request - Reduces boot time by 30-50%
- Cached in
storage/cache/config.php
Clear cache when updating config:
php siro config:clear
2. Route Caching#
Framework automatically caches routes:
- Routes compiled to PHP array
- Stored in
storage/cache/routes.php - Loaded instantly on subsequent requests
No manual action needed - caching is automatic.
3. Database Query Optimization#
// ❌ Bad - N+1 queries
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // Query per post!
}
// ✅ Good - 2 queries total
$posts = Post::with('author')->get();
foreach ($posts as $post) {
echo $post->author->name; // No additional queries
}
Schema::table('users', function (Blueprint $table) {
$table->index('email'); // Speed up WHERE email = ?
$table->index(['status', 'created_at']); // Composite index
});
// ❌ Bad - loads all records
$users = User::all();
// ✅ Good - pagination
$users = User::paginate(20, $page);
// ✅ Good - limit
$users = User::limit(100)->get();
// Cache query results for 60 seconds
$stats = DB::table('orders')
->selectRaw('SUM(total) as revenue')
->cache(60)
->first();
4. Response Compression#
Auto Gzip compression enabled by default:
- Reduces bandwidth by 60-80%
- Zero configuration required
- Client must send
Accept-Encoding: gzipheader
Verify compression:
curl -H "Accept-Encoding: gzip" -I https://yourdomain.com/api/users
# Should show: Content-Encoding: gzip
5. Queue Heavy Operations#
Offload slow tasks to background:
// Instead of sending email synchronously
Mail::to($user)->subject('Welcome')->html($html)->send(); // Blocks request
// Queue it
Mail::to($user)->subject('Welcome')->html($html)->queue(); // Returns immediately
Process queue:
# One-time processing
php siro queue:work
# Daemon mode (production)
php siro queue:work --daemon
# Crontab setup
* * * * * cd /path/to/project && php siro queue:work
6. Use Redis for High-Traffic Sites#
Switch cache driver:
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
Switch session driver:
SESSION_DRIVER=redis
Benefits:
- Faster than file-based cache
- Better for concurrent requests
- Supports distributed systems
🎯 Best Practices#
Request Lifecycle Optimization#
// ❌ Too many middleware layers
Route::get('/users', [UserController::class, 'index'])
->middleware([AuthMiddleware::class, LogMiddleware::class,
CorsMiddleware::class, JsonMiddleware::class]);
// ✅ Only essential middleware
Route::get('/users', [UserController::class, 'index'])
->middleware([AuthMiddleware::class]); // Cors/Json applied globally
// ✅ Container resolves dependencies automatically
class UserController {
public function __construct(
private UserService $service,
private Logger $logger
) {}
}
// ❌ Manual resolution (slower)
$service = new UserService(new UserRepository());
// ❌ Bad - runs on every instantiation
class UserController {
public function __construct() {
$this->data = DB::table('config')->get(); // Slow!
}
}
// ✅ Good - lazy loading
class UserController {
private ?Collection $data = null;
private function getConfig(): Collection {
if ($this->data === null) {
$this->data = DB::table('config')->get();
}
return $this->data;
}
}
Memory Management#
public function processLargeDataset() {
$data = DB::table('logs')->get(); // 100MB
// Process data
$result = $this->analyze($data);
// Free memory
unset($data);
return $result;
}
// ❌ Loads all into memory
foreach (User::all() as $user) {
process($user);
}
// ✅ Streams one at a time
foreach (User::cursor() as $user) {
process($user);
}
// Check current memory
$memory = memory_get_usage(true) / 1024 / 1024; // MB
Logger::debug("Memory usage: {$memory}MB");
// Peak memory
$peak = memory_get_peak_usage(true) / 1024 / 1024;
Logger::debug("Peak memory: {$peak}MB");
📈 Monitoring & Profiling#
Trace ID System#
Every request gets unique trace ID:
X-Siro-Trace-Id: siro_a1b2c3d4e5f6g7h8
X-Response-Time: 8.45ms
View slow requests:
# Show top 10 slowest requests (>100ms)
php siro slow
# Custom threshold
php siro slow --min=200 --limit=20
Output:
Top 10 slow requests (> 100ms):
+---+---------------------+--------+--------------------+--------+----------+-----+
| # | Time | Method | Path | Status | Duration | SQL |
+---+---------------------+--------+--------------------+--------+----------+-----+
| 1 | 2026-04-30 02:00:44 | POST | /api/auth/register | 201 | 103.6ms | 2 |
| 2 | 2026-04-30 01:55:12 | GET | /api/users | 200 | 245.8ms | 5 |
+---+---------------------+--------+--------------------+--------+----------+-----+
Investigate specific request:
php siro log:trace siro_a1b2c3d4e5f6g7h8
# Shows:
# - Request/response bodies
# - SQL queries with timing
# - Memory usage
# - Execution timeline
SQL Query Logging#
Enable slow query detection:
DB_SLOW_QUERY_THRESHOLD=100 # Log queries > 100ms
Logs to storage/logs/error.log:
Slow query (150.25ms): SELECT * FROM users WHERE email = :email
Bindings: {"email":"test@example.com"}
Application Metrics#
Track custom metrics:
use Siro\Core\Logger;
$start = microtime(true);
// Your code here
$result = $this->processData();
$duration = (microtime(true) - $start) * 1000;
Logger::debug('Processing completed', [
'duration_ms' => round($duration, 2),
'records_processed' => count($result),
]);
🚀 Production Deployment#
Pre-Deployment Checklist#
# 1. Optimize for production
php siro optimize
# Runs:
# - php siro config:cache
# - composer dump-autoload --optimize
# 2. Validate environment
php siro env:check
# 3. Run benchmarks
php siro benchmark
# 4. Clear development logs
rm -rf storage/logs/traces/*.json
Server Configuration#
server {
listen 80;
server_name api.example.com;
root /var/www/html/public;
index index.php;
# Enable gzip compression
gzip on;
gzip_types application/json text/xml;
gzip_min_length 1000;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
}
; /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
; Adjust based on available RAM
; Formula: max_children = Available RAM / Memory per process
; Example: 4GB RAM / 80MB per process = 50 children
Load Testing#
Use Apache Bench:
# 1000 requests, 100 concurrent
ab -n 1000 -c 100 https://api.example.com/api/users
# Expected output:
# Requests per second: 5000 [#/sec] (mean)
# Time per request: 20.000 [ms] (mean)
Use wrk:
# More accurate load testing
wrk -t12 -c400 -d30s https://api.example.com/api/users
# Output:
# Running 30s test @ https://api.example.com/api/users
# 12 threads and 400 connections
# Thread Stats Avg Stdev Max +/- Stdev
# Latency 20.00ms 5.00ms 100.00ms 80.00%
# Req/Sec 416.67 50.00 600.00 75.00%
# 150000 requests in 30.00s, 50.00MB read
# Requests/sec: 5000.00
# Transfer/sec: 1.67MB
🔧 Troubleshooting Performance Issues#
Problem: Slow Response Times#
Diagnosis:
# Check slow requests
php siro slow
# View specific trace
php siro log:trace <trace-id>
Common Causes:
- N+1 queries - Use eager loading
- Missing indexes - Add database indexes
- Heavy middleware - Reduce middleware chain
- Large result sets - Use pagination
- Synchronous operations - Move to queue
Problem: High Memory Usage#
Diagnosis:
// Add to controller
Logger::debug('Memory', [
'current' => memory_get_usage(true) / 1024 / 1024,
'peak' => memory_get_peak_usage(true) / 1024 / 1024,
]);
Solutions:
- Free large variables with
unset() - Use generators instead of arrays
- Limit query results
- Cache frequently accessed data
- Increase PHP memory limit if needed:
; php.ini
memory_limit = 256M
Problem: Database Bottleneck#
Diagnosis:
-- Check slow queries
SHOW PROCESSLIST;
-- Analyze query execution
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
Solutions:
- Add missing indexes
- Optimize queries (avoid SELECT *)
- Use read replicas for heavy reads
- Implement query caching
- Consider connection pooling
📊 Performance Comparison#
| Framework | Avg Ops/s | Memory | Dependencies | Boot Time |
|---|---|---|---|---|
| SiroPHP v0.35.0 | 398K | 2MB | 0 | <1ms |
| Laravel | 100-200 | 10-20MB | 50+ | 50-100ms |
| Slim | 5K-10K | 3-5MB | 5+ | 10-20ms |
| Lumen | 2K-5K | 4-8MB | 10+ | 20-40ms |
| Express.js | 10K-20K | 30-50MB | 100+ | 100-200ms |
SiroPHP is 2000-4000x faster than Laravel! 🚀
🎓 Advanced Optimization#
OPcache Configuration#
; php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0 ; Disable in production
opcache.revalidate_freq=0
opcache.interned_strings_buffer=16
Restart PHP-FPM after changes:
sudo systemctl restart php8.2-fpm
Preloading (PHP 7.4+)#
; php.ini
opcache.preload=/var/www/html/preload.php
opcache.preload_user=www-data
Create preload.php:
<?php
// Preload critical classes
require_once __DIR__ . '/vendor/autoload.php';
$classes = [
\Siro\Core\App::class,
\Siro\Core\Router::class,
\Siro\Core\Model::class,
\Siro\Core\Response::class,
];
foreach ($classes as $class) {
opcache_compile_file((new ReflectionClass($class))->getFileName());
}
HTTP/2 Support#
Enable in Nginx:
listen 443 ssl http2;
Benefits:
- Multiplexed requests
- Header compression
- Server push
- Faster page loads
📚 Additional Resources#
💡 Quick Wins#
Top 5 optimizations for immediate impact:
- Enable config caching -
php siro config:cache(30-50% faster boot) - Use eager loading - Eliminate N+1 queries (10-100x faster)
- Add database indexes - Speed up WHERE clauses (5-50x faster)
- Queue heavy operations - Return immediately to user
- Enable gzip compression - Reduce bandwidth 60-80%
Expected improvement: 5-10x faster response times with minimal effort!