11name : Benchmark Xdebug performance
22
33on :
4+ # This workflow runs on three triggers: Weekly on schedule, manually or when pull requests are labeled
45 schedule :
56 # Every Sunday at 03:00 UTC
67 - cron : ' 0 3 * * 0'
78 workflow_dispatch :
89 pull_request :
9- types : [opened, edited, reopened, synchronize ]
10+ types : [ labeled ]
1011
1112jobs :
1213 run-benchmark :
14+ # When triggered when a pull request is labeled we check the name of the label
1315 if : |
1416 github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' ||
1517 (
1618 github.event_name == 'pull_request' &&
17- (
18- startsWith(github.event.pull_request.title, 'perf:') ||
19- startsWith(github.event.pull_request.title, 'Perf:') ||
20- startsWith(github.event.pull_request.title, 'Performance:') ||
21- startsWith(github.event.pull_request.title, 'performance:')
22- )
19+ github.event.label.name == 'performance-check'
2320 )
2421 runs-on : ubuntu-latest
2522 strategy :
@@ -36,11 +33,13 @@ jobs:
3633 with :
3734 php-version : " ${{ matrix.php }}"
3835 coverage : none
36+ # We need to use a specific version of Composer because we are using a specific version of the Symfony Demo
37+ # which is not compatible with the latest versions of Composer
3938 tools : composer:v2.2.21
4039
41- # ASLR can cause a lot of noise due to missed sse opportunities for memcpy
42- # and other operations, so we disable it during benchmarking.
4340 - name : Disable ASLR
41+ # ASLR can cause a lot of noise due to missed sse opportunities for memcpy
42+ # and other operations, so we disable it during benchmarking.
4443 run : echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
4544
4645 - name : Install valgrind
5857
5958 - name : install Symfony demo
6059 if : matrix.command == 'symfony'
60+ # For versions of PHP greater than 8.4 we need to run a different version of the Symfony Demo.
61+ # The older versions suffer from the nulllable parameter deprecation and this generated a lot of deprecations
62+ # in the code and this skewed the tests results a lot. And we cannot use the newer version for older versions
63+ # of PHP as it is not compatible.
6164 run : |
6265 if [ "${{ matrix.php }}" \< "8.4" ]; then
6366 version="v2.0.2"
6770 git clone -q --branch "$version" --depth 1 https://github.com/symfony/demo.git ./symfony-demo
6871 cd symfony-demo
6972 composer install
73+ # We do not want our tests to be skewed by any error, warning, notice or deprecation thrown by the code,
74+ # so we set the error level to 0 on purpose. Unfortunately, Symfony overrides this setup and sets their own
75+ # error reporting level. We fix this by changing all the places where Symfony is setting the error reporting
76+ # level so that it continues to be set to 0
7077 find . -type f -name "*.php" -exec sed -i.bak -E 's/error_reporting\(.*\);/error_reporting(0);/g' {} +
7178
7279 - name : install Rector
@@ -97,19 +104,30 @@ jobs:
97104 if : matrix.command == 'symfony'
98105 run : |
99106 cd symfony-demo
107+ # The Symfony demo is run using php-cgi to simulate a web server. Symfony needs that this env variable is set,
108+ # it would usually be set by the web server, we need to set it manually instead
100109 export SCRIPT_FILENAME="$(realpath public/index.php)"
110+ # Symfony also needs this to be set to a non empty value
111+ export APP_SECRET=APP_SECRET
112+ # We run the web page twice so that the files have already been compiled into the opcache in order to remove
113+ # the compilation time from the equation. This also better reflects the normal situation in a web server
101114 php-cgi public/index.php
102115 valgrind --tool=callgrind --dump-instr=yes --callgrind-out-file=callgrind.out -- php-cgi public/index.php
103116 mv callgrind.out ..
104117 cd ..
105118
106119 - name : benchmark rector.php
107- if : matrix.command == 'rector'
120+ if : matrix.command == 'rector'
121+ # Rector needs to be run with the --xdebug flag so that it does not try to disable Xdebug
122+ # and also with the --debug flag so that it does not try to run several threads in parallel
108123 run : valgrind --tool=callgrind --dump-instr=yes --callgrind-out-file=callgrind.out -- php vendor/bin/rector --dry-run --xdebug --debug || true
109124
110125 - name : save matrix values
111126 run : |
127+ # We read the number of executed instructions from the callgrind.out file and save it in an artifact
128+ # to be processed later
112129 awk '/^summary:/ { print $2 }' callgrind.out > results/php-${{ matrix.php }}_cmd-${{ matrix.command }}_xdebug-${{ matrix.xdebug_mode }}.txt
130+ # We also save the matrix variables so that in a later step we can build a list of the executed options
113131 echo "${{ matrix.php }},${{ matrix.command }},${{ matrix.xdebug_mode }}" > results/matrix-values-${{ matrix.php }}-${{ matrix.command }}-${{ matrix.xdebug_mode }}.txt
114132
115133 - name : Upload result and matrix info
@@ -119,6 +137,8 @@ jobs:
119137 path : results/
120138
121139 performance :
140+ # After running all benchmarks for all commands, php versions and Xdebug modes we run a new job that builds
141+ # a summary of all execution times
122142 needs : run-benchmark
123143 runs-on : ubuntu-latest
124144 steps :
@@ -128,6 +148,8 @@ jobs:
128148 path : results
129149
130150 - name : Merge matrix values
151+ # We copy all the saved result files into a single folder and build a file with a list
152+ # of all matrix variables used
131153 run : |
132154 mkdir merged
133155 find results -name '*.txt' -exec cp {} merged/ \;
@@ -136,13 +158,16 @@ jobs:
136158
137159 - name : Generate summary table
138160 run : |
161+ # This is needed to be able to print the number of instructions using commas for separators
139162 export LC_NUMERIC=en_US.UTF-8
140163 echo "# 🕒 Performance Results" > summary.md
141164
165+ # We build lists of the different matrix options used in the previous jobs
142166 commands=$(cut -d',' -f2 unique-matrix-values.txt | sort | uniq)
143167 php_versions=$(cut -d',' -f1 unique-matrix-values.txt | sort | uniq)
144168 xdebug_modes=$(cut -d',' -f3 unique-matrix-values.txt | sort | uniq)
145169
170+ # And we loop over all these versions building a summary with tables of all the values
146171 for command in $commands; do
147172 echo "" >> summary.md
148173 echo "## **Command:** \`$command\`" >> summary.md
@@ -153,6 +178,8 @@ jobs:
153178 echo "| Xdebug | Instructions | Slowdown |" >> summary.md
154179 echo "|--------|-------------:|---------:|" >> summary.md
155180
181+ # To calculate the slowdown, we read a base value which is taken from the data
182+ # where Xdebug mode is "no" (it was not loaded)
156183 base_file="merged/php-${php}_cmd-${command}_xdebug-no.txt"
157184 base_value=$(cat "$base_file")
158185 for xdebug in $xdebug_modes; do
@@ -164,6 +191,7 @@ jobs:
164191 else
165192 slowdown=$(awk -v v=$value -v b=$base_value 'BEGIN { printf "%.1f%%", ((v - b) * 100) / b }')
166193 fi
194+ # The number of instructions is formatted with thousands separators
167195 formatted_value=$(printf "%'d" "$value")
168196 echo "| $xdebug | $formatted_value | $slowdown |" >> summary.md
169197 fi
@@ -172,9 +200,13 @@ jobs:
172200 done
173201
174202 - name : print summary
203+ # This generated summary file is copied into a special file which GitHub has defined
204+ # in this special env variable and it will use it to print a summary for the workflow
175205 run : cat summary.md >> $GITHUB_STEP_SUMMARY
176206
177207 - name : delete intermediary artifacts
208+ # The intermediate files that we generated are not needed and can be deleted,
209+ # helping us keep the summary page clean
178210 uses : geekyeggo/delete-artifact@v5
179211 with :
180212 name : results-*
0 commit comments