Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 31 additions & 31 deletions .github/benchmark-files/generate_summary.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*
* This script reads benchmark results from the results directory, merges them
* into a single directory, and generates a markdown summary with performance
* metrics for different PHP versions, commands, and Php-Debugger modes.
* metrics for different PHP versions, commands, and PHP Debugger modes.
*/

// Create merged directory and copy all result files into it
Expand Down Expand Up @@ -83,7 +83,7 @@
sort($commands);
sort($phpVersions);

// Sort Php-Debugger modes according to the defined order, leaving only those which actually exist in the data
// Sort PHP Debugger modes according to the defined order, leaving only those which actually exist in the data
$phpDebuggerModeOrder = ["no", "off", "debug"];
$phpDebuggerModes = array_values(array_filter($phpDebuggerModeOrder, function($mode) use ($phpDebuggerModes) {
return in_array($mode, $phpDebuggerModes);
Expand All @@ -92,7 +92,7 @@
// Start building the markdown summary and CSV data
$output = "# 🕒 Performance Results\n";
$csvData = [];
$csvData[] = ['command', 'php', 'php_debugger_mode', 'instructions', 'slowdown'];
$csvData[] = ['command', 'php', 'php_debugger_mode', 'instructions', 'overhead'];

// Loop through each command
foreach ($commands as $command) {
Expand All @@ -101,10 +101,10 @@
// Loop through each PHP version
foreach ($phpVersions as $php) {
$output .= "\n### **PHP Version:** `$php`\n\n";
$output .= "| Php-Debugger | Instructions | Slowdown | Improvement |\n";
$output .= "| PHP Debugger | Instructions | Overhead | Improvement |\n";
$output .= "|--------------|-------------:|---------:|------------:|\n";

// Get base value (when Php-Debugger mode is "no")
// Get base value (when PHP Debugger mode is "no")
$baseFile = "merged/php-{$php}_cmd-{$command}_php_debugger-no.txt";
if (!file_exists($baseFile)) {
fwrite(STDERR, "Warning: Base file not found: $baseFile\n");
Expand All @@ -113,7 +113,7 @@

$baseValue = (int)trim(file_get_contents($baseFile));

// Loop through each Php-Debugger mode
// Loop through each PHP Debugger mode
foreach ($phpDebuggerModes as $phpDebugger) {
$file = "merged/php-{$php}_cmd-{$command}_php_debugger-{$phpDebugger}.txt";

Expand All @@ -123,13 +123,13 @@

$value = (int)trim(file_get_contents($file));

// Calculate slowdown
// Calculate overhead
if ($phpDebugger === 'no') {
$slowdown = '0%';
$slowdownPercent = 0.0;
$overhead = '0%';
$overheadPercent = 0.0;
} else {
$slowdownPercent = (($value - $baseValue) * 100) / $baseValue;
$slowdown = sprintf('%.1f%%', $slowdownPercent);
$overheadPercent = (($value - $baseValue) * 100) / $baseValue;
$overhead = sprintf('%.1f%%', $overheadPercent);
}

// Format the value with thousands separators for markdown
Expand All @@ -139,19 +139,19 @@
$performanceChange = '--';
$key = $command . '-' . $php . '-' . $phpDebugger;
if (isset($previousResults[$key])) {
$previousSlowdown = $previousResults[$key];
$previousOverhead = $previousResults[$key];
$performanceChange = '0%';
if ($previousSlowdown !== 0) {
$currentSlowdown = sprintf('%.1f', $slowdownPercent);
$changePercent = (($previousSlowdown - $currentSlowdown) * 100) / $previousSlowdown;
if ($previousOverhead !== 0) {
$currentOverhead = sprintf('%.1f', $overheadPercent);
$changePercent = (($previousOverhead - $currentOverhead) * 100) / $previousOverhead;
$performanceChange = sprintf('%+.1f%%', $changePercent);
}
}

$output .= "| $phpDebugger | $formattedValue | $slowdown | $performanceChange |\n";
$output .= "| $phpDebugger | $formattedValue | $overhead | $performanceChange |\n";

// Add to CSV data (with raw numbers, not formatted)
$csvData[] = [$command, $php, $phpDebugger, $value, sprintf('%.1f', $slowdownPercent)];
$csvData[] = [$command, $php, $phpDebugger, $value, sprintf('%.1f', $overheadPercent)];
}
}
}
Expand All @@ -160,13 +160,13 @@
$output .= "\n# Performance Results Summary\n";
$output .= "\nThese tables show aggregated results across all PHP versions:\n";

// Aggregate data across all PHP versions for each command and Php-Debugger mode
// Aggregate data across all PHP versions for each command and PHP Debugger mode
foreach ($commands as $command) {
$output .= "\n## **Command:** `$command`\n\n";
$output .= "| Php-Debugger | Slowdown | Improvement |\n";
$output .= "| PHP Debugger | Overhead | Improvement |\n";
$output .= "|--------------|---------:|------------:|\n";

// Calculate aggregated values for each Php-Debugger mode
// Calculate aggregated values for each PHP Debugger mode
$aggregatedData = [];
foreach ($phpDebuggerModes as $phpDebugger) {
$totalInstructions = 0;
Expand All @@ -187,16 +187,16 @@
$totalBaseInstructions += $baseValue;
$count++;

$slowdownPercent = (($value - $baseValue) * 100) / $baseValue;
$overheadPercent = (($value - $baseValue) * 100) / $baseValue;

// Calculate performance change if previous data exists
$key = $command . '-' . $php . '-' . $phpDebugger;
if (isset($previousResults[$key])) {
$previousSlowdown = $previousResults[$key];
$previousOverhead = $previousResults[$key];
$changePercent = 0;
if ($previousSlowdown !== 0) {
$currentSlowdown = sprintf('%.1f', $slowdownPercent);
$changePercent = (($previousSlowdown - $currentSlowdown) * 100) / $previousSlowdown;
if ($previousOverhead !== 0) {
$currentOverhead = sprintf('%.1f', $overheadPercent);
$changePercent = (($previousOverhead - $currentOverhead) * 100) / $previousOverhead;
}
$totalPerformanceChange += $changePercent;
$performanceChangeCount++;
Expand All @@ -205,12 +205,12 @@
}

if ($count > 0) {
// Calculate average slowdown
// Calculate average overhead
if ($phpDebugger === 'no') {
$avgSlowdown = '0%';
$avgOverhead = '0%';
} else {
$avgSlowdownPercent = (($totalInstructions - $totalBaseInstructions) * 100) / $totalBaseInstructions;
$avgSlowdown = sprintf('%.1f%%', $avgSlowdownPercent);
$avgOverheadPercent = (($totalInstructions - $totalBaseInstructions) * 100) / $totalBaseInstructions;
$avgOverhead = sprintf('%.1f%%', $avgOverheadPercent);
}

// Calculate average performance change
Expand All @@ -220,7 +220,7 @@
$performanceChange = sprintf('%+.1f%%', $avgPerformanceChange);
}

$output .= "| $phpDebugger | $avgSlowdown | $performanceChange |\n";
$output .= "| $phpDebugger | $avgOverhead | $performanceChange |\n";
}
}
}
Expand Down Expand Up @@ -404,7 +404,7 @@ function fetchPreviousBenchmarkResults(): array {
}

if (count($fields) >= 4) {
// Fields: command, php, php_debugger_mode, instructions, slowdown
// Fields: command, php, php_debugger_mode, instructions, overhead
$key = $fields[0] . '-' . $fields[1] . '-' . $fields[2];
$previousData[$key] = (int)$fields[4];
}
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Benchmark Php-Debugger performance
name: Benchmark PHP Debugger performance

on:
# This workflow runs on three triggers: Weekly on schedule, manually, or when pull requests are labeled
Expand Down Expand Up @@ -95,7 +95,7 @@ jobs:
sudo cp ./.github/benchmark-files/php-debugger-benchmark.ini /etc/php/${{ matrix.php }}/cli/conf.d
sudo cp ./.github/benchmark-files/php-debugger-benchmark.ini /etc/php/${{ matrix.php }}/cgi/conf.d

- name: Set Php-Debugger mode
- name: Set PHP Debugger mode
run: echo "PHP_DEBUGGER_MODE=${{ matrix.php_debugger_mode }}" >> $GITHUB_ENV

- name: Confirm PHP setup
Expand Down Expand Up @@ -126,7 +126,7 @@ jobs:

- name: benchmark rector.php
if: matrix.command == 'rector'
# Rector needs to be run with the --xdebug flag so that it does not try to disable Php-Debugger and also with
# Rector needs to be run with the --xdebug flag so that it does not try to disable PHP Debugger and also with
# the --debug flag so that it does not try to run several threads in parallel
run: valgrind --tool=callgrind --dump-instr=yes --callgrind-out-file=callgrind.out -- php vendor/bin/rector --dry-run --xdebug --debug || true

Expand All @@ -146,7 +146,7 @@ jobs:
path: results/

performance:
# After running all benchmarks for all commands, PHP versions and Php-Debugger modes, we run a new job that builds a
# After running all benchmarks for all commands, PHP versions and PHP Debugger modes, we run a new job that builds a
# summary of all execution times
needs: run-benchmark
runs-on: ubuntu-latest
Expand Down
46 changes: 39 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,52 @@ A lightweight, high-performance PHP debugger extension. Forked from [Xdebug](htt

## Why PHP Debugger?

- **+4% overhead** when loaded but inactive (vs +324% in Xdebug)
- **97% performance improvement over Xdebug** when loaded but inactive
- **Drop-in Xdebug replacement** — existing configs, IDE setups, and workflows work unchanged
- **Debug-only** — no profiler, no coverage, no tracing. Just debugging.
- **Full DBGp protocol support** — works with PhpStorm, VS Code, and any DBGp-compatible IDE

### Benchmarks

`bench.php` on Apple Silicon, PHP 8.5.3. Extension loaded, xdebug.mode=debug, no IDE connected.
The following benchmarks were run in GitHub's CI (GitHub Actions) environment using a standard Ubuntu runner.
The performance was measured using Valgrind to count the number of executed instructions.
This is much more precise and reproducible than timing execution runs. All measuremments were done
using all supported PHP versions (the number shown is the average), with the extension loaded and
no IDE connected.

| Configuration | Time | Overhead |
|---------------|--------|-----------|
| No debugger | 0.139s | — |
| PHP Debugger | 0.145s | **+4%** |
| Xdebug | 0.589s | **+324%** |
We measured three different scenarios which we believe represent a good mix of typical PHP operations:

- `bench.php`: a syntetic benchmark that runs a number of computationally heavy functions

| Configuration | Overhead |
|---------------|------------:|
| No debugger | — |
| Xdebug | **+661.6%** |
| PHP Debugger | **+12.9%** |

Improvement: **98.0%**

- `Rector`: running a RectorPHP rule on a PHP file

| Configuration | Overhead |
|---------------|------------:|
| No debugger | — |
| Xdebug | **+124.5%** |
| PHP Debugger | **+3.6%** |

Improvement: **97.1%**

- `Symfony`: running a basic request on a Symfony demo project

| Configuration | Overhead |
|---------------|-----------:|
| No debugger | — |
| Xdebug | **+35.3%** |
| PHP Debugger | **+1.3%** |

Improvement: **96.1%**

Overall improvement: **97.1%**

## Installation

Expand Down
Loading