Skip to content

Performance issue running coverage on Windows #1062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jrfnl opened this issue Feb 7, 2025 · 47 comments
Open

Performance issue running coverage on Windows #1062

jrfnl opened this issue Feb 7, 2025 · 47 comments

Comments

@jrfnl
Copy link
Contributor

jrfnl commented Feb 7, 2025

Q A
PHPUnit version 12.0.1
PHP version 8.4
Installation Method PHAR (but probably Composer to)

Summary

I'm seeing a significant performance slow-down when using PHPUnit 12 for path/branch coverage versus PHPUnit 11 when running on Windows (10).

When running just one test class (filtered down), I see a difference of 3 vs 50 seconds.

The slow-down appears to be at the start, before progress is being shown.

Current behavior

$ phpunit11 -c phpunit10.xml --filter AssertObjectNotEquals

PHPUnit 11.5.7 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.1 with Xdebug 3.4.1
Configuration: path/to/PHPUnit-Polyfills/phpunit10.xml

.........S.....S.......                                           23 / 23 (100%)

Time: 00:03.563, Memory: 376.00 MB

OK, but some tests were skipped!
Tests: 23, Assertions: 48, Skipped: 2.

Generating code coverage report in HTML format ... done [00:00.741]


Code Coverage Report Summary:
  Classes:  4.55% (1/22)
  Methods:  4.41% (6/136)
  Paths:    5.94% (6/101)
  Branches:    5.00% (6/120)
  Lines:    1.80% (10/555)
$ phpunit12 -c phpunit10.xml --filter AssertObjectNotEquals

PHPUnit 12.0.1 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.1 with Xdebug 3.4.1
Configuration: path/to/PHPUnit-Polyfills/phpunit10.xml

.........S.....S.......                                           23 / 23 (100%)

Time: 00:50.153, Memory: 370.00 MB

OK, but some tests were skipped!
Tests: 23, Assertions: 48, Skipped: 2.

Generating code coverage report in HTML format ... done [00:00.422]


Code Coverage Report Summary:
  Classes:  4.55% (1/22)
  Methods:  4.41% (6/136)
  Paths:    7.32% (6/82)
  Branches:    6.19% (6/97)
  Lines:    1.80% (10/555)

How to reproduce

If I run with --debug on, it looks like the test suite "hangs" between "Test Passed" and "Test Finished"

Test Runner Execution Started (23 tests)
Test Suite Started (path/to/PHPUnit-Polyfills/phpunit10.xml, 23 tests)
Test Suite Started (PHPUnitPolyfills, 23 tests)
Test Suite Started (Yoast\PHPUnitPolyfills\Tests\Polyfills\AssertObjectNotEqualsTest, 21 tests)
Test Preparation Started (Yoast\PHPUnitPolyfills\Tests\Polyfills\AssertObjectNotEqualsTest::testAssertObjectNotEquals)
Test Prepared (Yoast\PHPUnitPolyfills\Tests\Polyfills\AssertObjectNotEqualsTest::testAssertObjectNotEquals)
Test Passed (Yoast\PHPUnitPolyfills\Tests\Polyfills\AssertObjectNotEqualsTest::testAssertObjectNotEquals)

....hanging....

Test Finished (Yoast\PHPUnitPolyfills\Tests\Polyfills\AssertObjectNotEqualsTest::testAssertObjectNotEquals)
Test Preparation Started (Yoast\PHPUnitPolyfills\Tests\Polyfills\AssertObjectNotEqualsTest::testAssertObjectNotEqualsCustomMethodName)

Reproducible via the https://github.com/Yoast/PHPUnit-Polyfills repo (though I'm still making some updates for PHPUnit 12 compatibility, which will be pulled soonish).

Expected behavior

No significant slow-down in performance.

@jrfnl jrfnl added the type/bug label Feb 7, 2025
@sebastianbergmann sebastianbergmann changed the title PHPUnit 12 | Performance issue running coverage on Windows Performance issue running coverage on Windows Feb 8, 2025
@sebastianbergmann
Copy link
Owner

There have been no changes (that I am aware of) that are specific to branch/path coverage.

From your --debug output (thank you for that!) I assume that it always hangs after the execution of the first test has finished. After "Test Passed" and before "Test Finished" the collection of code coverage data is stopped and the collected data is processed. The first code coverage data is processed during the execution of a test suite there is now additional static analysis performed which could explain the "hanging". However, this static analysis is the same regardless of whether branch/path coverage is enabled or not. The implementation of https://github.com/sebastianbergmann/php-code-coverage/blob/main/src/Target/MapBuilder.php is not optimized and its result is not yet cached, I have this on my TODO for after the release anyway.

Before I go down the wrong rabbit hole, can you please check whether you also get the "hanging" when branch/path coverage is disabled?

@sebastianbergmann sebastianbergmann transferred this issue from sebastianbergmann/phpunit Feb 8, 2025
@sebastianbergmann
Copy link
Owner

@staabm Can you have a look at https://github.com/sebastianbergmann/php-code-coverage/blob/main/src/Target/MapBuilder.php and see if/how this could be optimized? Thanks!

@staabm
Copy link
Contributor

staabm commented Feb 8, 2025

I can have a look after Juliette pushed the phpunit 12 changes for the reproducer

@sebastianbergmann
Copy link
Owner

I can have a look after Juliette pushed the phpunit 12 changes for the reproducer

I think any project can be used to look into the performance of MapBuilder. And the map it builds is the same for PCOV and Xdebug.

@staabm
Copy link
Contributor

staabm commented Feb 8, 2025

I was not yet able to reproduce the slow run on https://github.com/Yoast/PHPUnit-Polyfills (I have no experience with branch coverage):

➜  PHPUnit-Polyfills git:(3.x) ✗ vendor/bin/phpunit -c phpunit10.xml.dist --filter AssertObjectNotEquals --path-coverage
PHPUnit 12.0.1 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.3.16 with Xdebug 3.4.1
Configuration: /Users/staabm/workspace/PHPUnit-Polyfills/phpunit10.xml.dist

WWWWWWWWWSSWWSWSWWWWW..                                           23 / 23 (100%)

Time: 00:00.732, Memory: 158.00 MB

do you think we can trigger the slow path without xdebug beeing loaded (e.g. with pre-recorded information)?

running blackfire and xdebug in the same process usually doesn't work, as these step onto each others feet.

@sebastianbergmann
Copy link
Owner

I think we should wait until Juliette answered my "can you please check whether you also get the "hanging" when branch/path coverage is disabled?" question. I am almost certain that this is not related to branch/path coverage.

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

Before I go down the wrong rabbit hole, can you please check whether you also get the "hanging" when branch/path coverage is disabled?

@sebastianbergmann I've just checked and with only line coverage and filtered down to one test class, I see a small, but not a significant slow down.


Logs
$ phpunit11 -c phpunit10.xml --filter AssertObjectNotEquals

PHPUnit 11.5.7 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.1 with Xdebug 3.4.1
Configuration: path/to/PHPUnit-Polyfills/phpunit10.xml

.........S.....S.......                                           23 / 23 (100%)

Time: 00:00.534, Memory: 52.00 MB

OK, but some tests were skipped!
Tests: 23, Assertions: 48, Skipped: 2.

Generating code coverage report in HTML format ... done [00:00.473]


Code Coverage Report Summary:
  Classes:  4.55% (1/22)
  Methods:  4.41% (6/136)
  Lines:    1.80% (10/555)
$ phpunit12 -c phpunit10.xml --filter AssertObjectNotEquals

PHPUnit 12.0.1 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.1 with Xdebug 3.4.1
Configuration: path/to/PHPUnit-Polyfills/phpunit10.xml

.........S.....S.......                                           23 / 23 (100%)

Time: 00:01.452, Memory: 54.00 MB

OK, but some tests were skipped!
Tests: 23, Assertions: 48, Skipped: 2.

Generating code coverage report in HTML format ... done [00:00.196]


Code Coverage Report Summary:
  Classes:  4.55% (1/22)
  Methods:  4.41% (6/136)
  Lines:    1.80% (10/555)

When running the full suite with only line coverage, I see no significant difference in the performance.


Logs
$ phpunit11 -c phpunit10.xml

PHPUnit 11.5.7 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.1 with Xdebug 3.4.1
Configuration: path/to/PHPUnit-Polyfills/phpunit10.xml

...............................................................  63 / 478 ( 13%)
................................................SSSS....SSSS... 126 / 478 ( 26%)
.SSSSSSSS.................................SSSSSSSSSSSS......... 189 / 478 ( 39%)
............................................................... 252 / 478 ( 52%)
............................................................... 315 / 478 ( 65%)
......................S.....S..............S.....S......SSS.... 378 / 478 ( 79%)
SSS............................................................ 441 / 478 ( 92%)
..............................

Time: 00:03.360, Memory: 54.00 MB

OK, but some tests were skipped!
Tests: 471, Assertions: 862, Skipped: 39.

Generating code coverage report in HTML format ... done [00:00.447]


Code Coverage Report Summary:
  Classes:  9.09% (2/22)
  Methods:  5.15% (7/136)
  Lines:    2.88% (16/555)
$ phpunit12 -c phpunit10.xml

PHPUnit 12.0.1 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.1 with Xdebug 3.4.1
Configuration: path/to/PHPUnit-Polyfills/phpunit10.xml

...............................................................  63 / 478 ( 13%)
................................................SSSS....SSSS... 126 / 478 ( 26%)
.SSSSSSSS.................................SSSSSSSSSSSS......... 189 / 478 ( 39%)
............................................................... 252 / 478 ( 52%)
............................................................... 315 / 478 ( 65%)
......................S.....S..............S.....S......SSS.... 378 / 478 ( 79%)
SSS............................................................ 441 / 478 ( 92%)
..............................

Time: 00:03.985, Memory: 54.00 MB

OK, but some tests were skipped!
Tests: 471, Assertions: 862, Skipped: 39.

Generating code coverage report in HTML format ... done [00:00.203]


Code Coverage Report Summary:
  Classes:  9.09% (2/22)
  Methods:  5.15% (7/136)
  Lines:    2.88% (16/555)

So, for argument's sake, I checked the performance of path/branch coverage when running the full test suite as well, and the slow down is back, so that confirms it's the path/branch coverage which is the source of the slow-down, not the test filtering.

Running the full test suite vs a filtered down test doesn't seem to make a difference in the amount of slow down though.


Logs
$ phpunit11 -c phpunit10.xml

PHPUnit 11.5.7 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.1 with Xdebug 3.4.1
Configuration: D:\000_GitHub\WordPress_SEO_by_Yoast\PHPUnit-Polyfills\phpunit10.xml

...............................................................  63 / 478 ( 13%)
................................................SSSS....SSSS... 126 / 478 ( 26%)
.SSSSSSSS.................................SSSSSSSSSSSS......... 189 / 478 ( 39%)
............................................................... 252 / 478 ( 52%)
............................................................... 315 / 478 ( 65%)
......................S.....S..............S.....S......SSS.... 378 / 478 ( 79%)
SSS............................................................ 441 / 478 ( 92%)
..............................

Time: 00:46.100, Memory: 376.00 MB

OK, but some tests were skipped!
Tests: 471, Assertions: 862, Skipped: 39.

Generating code coverage report in HTML format ... done [00:00.724]


Code Coverage Report Summary:
  Classes:  9.09% (2/22)
  Methods:  5.15% (7/136)
  Paths:    6.82% (9/132)
  Branches:    8.12% (13/160)
  Lines:    2.88% (16/555)
$ phpunit12 -c phpunit10.xml

PHPUnit 12.0.1 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.1 with Xdebug 3.4.1
Configuration: D:\000_GitHub\WordPress_SEO_by_Yoast\PHPUnit-Polyfills\phpunit10.xml

...............................................................  63 / 478 ( 13%)
................................................SSSS....SSSS... 126 / 478 ( 26%)
.SSSSSSSS.................................SSSSSSSSSSSS......... 189 / 478 ( 39%)
............................................................... 252 / 478 ( 52%)
............................................................... 315 / 478 ( 65%)
......................S.....S..............S.....S......SSS.... 378 / 478 ( 79%)
SSS............................................................ 441 / 478 ( 92%)
..............................

Time: 01:43.787, Memory: 370.00 MB

OK, but some tests were skipped!
Tests: 471, Assertions: 862, Skipped: 39.

Generating code coverage report in HTML format ... done [00:00.457]


Code Coverage Report Summary:
  Classes:  9.09% (2/22)
  Methods:  5.15% (7/136)
  Paths:    7.96% (9/113)
  Branches:    9.49% (13/137)
  Lines:    2.88% (16/555)

I can have a look after Juliette pushed the phpunit 12 changes for the reproducer

@staabm The WIP branch I'm using is available, though not yet merged: https://github.com/Yoast/PHPUnit-Polyfills/commits/feature/composer-ci-allow-for-phpunit-12/

Just FYI/I'll figure out some hacky solution - The thing I'm running into is something which was fixed in PHPUnit 11.3.? (non-existent traits not being ignored, but causing a fatal). As this package polyfills a PHPUnit 11.2.0 assertion (assertObjectNotEquals()), the tests need to run on PHPUnit < 11.2 + > 11.2. What with 12 now enforcing CoversTrait for traits and the CoversTrait attribute not being available in PHPUnit < 11.2 AND PHPUnit < 11.3.? not ignoring unknown attributes... the tests won't run on PHPUnit < 11.2, even when not running with code coverage.

I suspect I'll end up creating PHPUnit version specific wrappers for the tests + adding another config file. Nothing to worry about though as this is an edge-case which should not affect "normal" packages.

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

I was not yet able to reproduce the slow run on https://github.com/Yoast/PHPUnit-Polyfills (I have no experience with branch coverage):

@staabm In case it helps, this is the exact config I used locally:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
    backupGlobals="true"
    bootstrap="./tests/bootstrap.php"
    beStrictAboutOutputDuringTests="true"
    beStrictAboutTestsThatDoNotTestAnything="true"
    colors="true"
    cacheDirectory=".cache/phpunit.cache"
    requireCoverageMetadata="true"
    displayDetailsOnTestsThatTriggerErrors="true"
    displayDetailsOnTestsThatTriggerWarnings="true"
    displayDetailsOnTestsThatTriggerNotices="true"
    displayDetailsOnTestsThatTriggerDeprecations="true"
    displayDetailsOnPhpunitDeprecations="true"
    failOnWarning="true"
    failOnNotice="true"
    failOnDeprecation="true"
    failOnPhpunitDeprecation="false"
    >
    <testsuites>
        <testsuite name="PHPUnitPolyfills">
            <directory>./tests/</directory>
        </testsuite>
    </testsuites>

    <source>
        <include>
            <file>phpunitpolyfills-autoload.php</file>
            <directory suffix=".php">./src/</directory>
        </include>
        <exclude>
            <file>src/Polyfills/AssertClosedResource_Empty.php</file>
        </exclude>
    </source>

    <coverage pathCoverage="true">
        <report>
            <text outputFile="php://stdout" showOnlySummary="true"/>
            <html outputDirectory="build/coverage-html" lowUpperBound="35" highLowerBound="90"/>
        </report>
    </coverage>
</phpunit>

@staabm
Copy link
Contributor

staabm commented Feb 8, 2025

which xdebug.mode do you use?

I am still unable to reproduce with

php -v
PHP 8.3.16 (cli) (built: Jan 14 2025 18:25:29) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.16, Copyright (c) Zend Technologies
    with Xdebug v3.4.1, Copyright (c) 2002-2025, by Derick Rethans

using vendor/bin/phpunit -c phpunit10.xml.dist --filter AssertObjectNotEquals and your given config

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

which xdebug.mode do you use?

@staabm This the config I'm using for PHP 8.4.1 (which I used for the above tests):

[xdebug]
zend_extension="C:/wamp/bin/php/ext/xdebug/php_xdebug-3.4.1-8.4-ts-vs17-x86_64.dll"

;xdebug.mode allowed are : off develop coverage debug gcstats profile trace
xdebug.mode=coverage
xdebug.output_dir="C:/wamp/tmp"
xdebug.show_local_vars=0
xdebug.log="C:/wamp/logs/xdebug.log"
;xdebug.log_level : 0 Criticals, 1 Connection, 3 Warnings, 5 Communication, 7 Information, 10 Debug	Breakpoint
xdebug.log_level=7
xdebug.profiler_output_name=trace.%H.%t.%p.cgrind
xdebug.use_compression=false

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

For argument's sake just reran the tests with PHP 8.3.14 (same Xdebug config) and got the same results.

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

@staabm Another thought, though I can't imagine it would make a difference - I'm running the tests with the PHAR file, while it looks l like you're trying to reproduce with a Composer installed version.

Update: just tested and to my dismay and surprise - I cannot reproduce with a Composer install either. So the PHAR in combination with path/branch coverage is where things are going wrong...

Colour me flabbergasted.

@sebastianbergmann
Copy link
Owner

There seems to be something broken with regard to the static analysis cache when the PHAR is used. Although different, sebastianbergmann/phpunit#6124 seems to point into that direction as well.

@sebastianbergmann
Copy link
Owner

@jrfnl Can you delete your .phpunit.cache directory and see if that works around this issue as well? Thanks!

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

Can you delete your .phpunit.cache directory and see if that works around this issue as well? Thanks!

Done (including deleting various other files, like PHPUnit 9 cache file and code coverage result files) and it unfortunately doesn't make a difference.

@sebastianbergmann
Copy link
Owner

Please test whether the PHPUnit 12.0.2 PHAR solves this issue for you. Thank you!

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

@sebastianbergmann Tested and unfortunately, no luck.

@sebastianbergmann
Copy link
Owner

Thank you for testing. I will (try to) stop working on this until after the weekend. Besides, I have no idea what might be going on here as I looked into the changes between 11.0 and main and am unable to find anything specific to branch/path coverage.

In general, I can only advise against using branch/path coverage "all the time" (as in configured in your XML configuration file). But, of course, that is not a solution to the problem you discovered.

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

@sebastianbergmann Sounds like a good plan. Wishing you a relaxing weekend!

Let me know if/when you want me to try additional debug steps. Happy to oblige.

In general, I can only advise against using branch/path coverage "all the time" (as in configured in your XML configuration file). But, of course, that is not a solution to the problem you discovered.

Agreed. The default config for projects I manage is generally set to line coverage. It's only my local override config in which I have path/branch coverage enabled and even then, I normally only run coverage locally when making significant changes. Or, like in this case, when debugging an issue with code coverage ;-)

@sebastianbergmann
Copy link
Owner

Let me know if/when you want me to try additional debug steps.

The only thing I can think of right now would be to use git-bisect to find out which commit in php-code-coverage between 11.0 and 12.0.0 causes the slowdown. However, as this issue seems to only occur when PHPUnit is used from a PHAR this means that for each bisect step a PHAR needs to be built. I do not have a ready-to-use automation to make this convenient :-(

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

🤔 I seem to remember setting up making PHARs available as artifacts for the builds in the PHPUnit repo. I might be able to work something out from that.

@sebastianbergmann
Copy link
Owner

ant phar-snapshot in a checkout of PHPUnit's repository will build a PHAR. You "only" need to make sure that php-code-coverage is installed from a specific revision.

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

Good to know, though I've never managed to get ant working on Windows... (and the installation instructions are abysmal). I'll have a try anyway.

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

Yeah, that's not going to work. I've managed to get ant running on Windows via Chocolatey now, but the commands to run the tools in build.xml are not compatible with Windows.

BUILD FAILED
path/to/phpunit\build.xml:18: Execute failed: java.io.IOException: Cannot run program "path/to/phpunit\tools\composer" (in directory "path/to/phpunit"): CreateProcess error=193, %1 is not a valid Win32 application

@sebastianbergmann
Copy link
Owner

Sorry, but you need something UNIXy to build PHARs.

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

Sorry, but you need something UNIXy to build PHARs.

... for PHPUnit ;-)

Generated some PHARs via GH Actions now with PHPUnit at dev-main and code cov at various commit hashes, but most of those PHARs aren't working as PHPUnit 12 presumes certain changes in the code cov package.

These are the commit hashes I used (from oldest to most recent):

  • "46af700c350468657001c67c9d5c4e6d4f0057a8" ❌
  • "873416b3e559601ade441f0f4ae210d7a13e2711" ❌
  • "9354b267618453a43fa63af72899930e48022fa4" ❌
  • "391889d0b6f626e0379396ed61e2c6ae92beba10" ❌
  • "5ed521f486312c3b1b970da6a6543e641fa32578" ❌
  • "08fbab6175db74f68d528342cbdab254e74fd593" ❌
  • "0f52179519a10d3edab3cf567e909d68d48529ce" 🏃 [Edit: this ran the first time I tried, but not on subsequent runs]
  • "69dde560510151f7d04315fac6c72016cc5d7bc8" 🏃

Only the last two commits I tried yield a working PHAR and both of those have the slow down.

@jrfnl
Copy link
Contributor Author

jrfnl commented Feb 8, 2025

Looked at it with fresh eyes and found another cache file. Deleting that one does fix things and gets rid of the slow-down.

Also, once it's gone, re-running on PHPUnit 11 (which creates the cache anew) and then PHPUnit 12 without clearing the cache in between, re-introduces the issue.

Hope this gets us another step closer.

@verfriemelt-dot-org
Copy link

i encountered the same slowdown issue on a large private codebase running linux :/
i noticed it when our pipeline failed due to timeouts and i can reproduce that locally too after wiping all caches.

i do not use phpunit as phar, so maybe i can try to help with the bisect.

@verfriemelt-dot-org
Copy link

 $  XDEBUG_MODE=coverage phpunit --testsuite=Application --coverage-text --filter=VuSmoke

PHPUnit 11.5.12 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.3.19 with Xdebug 3.4.1
Configuration: /home/easteregg/src/eos/tickeos/core/phpunit.dist.xml

.                                                                   1 / 1 (100%)

Time: 00:07.442, Memory: 82.75 MB

with upgrading to 12

 - Removing sebastian/code-unit-reverse-lookup (4.0.1)
 - Removing sebastian/code-unit (3.0.2)
 - Upgrading sebastian/diff (6.0.2 => 7.0.0): Extracting archive
 - Upgrading sebastian/version (5.0.2 => 6.0.0): Extracting archive
 - Upgrading sebastian/type (5.1.0 => 6.0.0): Extracting archive
 - Upgrading sebastian/recursion-context (6.0.2 => 7.0.0): Extracting archive
 - Upgrading sebastian/object-reflector (4.0.1 => 5.0.0): Extracting archive
 - Upgrading sebastian/object-enumerator (6.0.1 => 7.0.0): Extracting archive
 - Upgrading sebastian/global-state (7.0.2 => 8.0.0): Extracting archive
 - Upgrading sebastian/exporter (6.3.0 => 7.0.0): Extracting archive
 - Upgrading sebastian/environment (7.2.0 => 8.0.0): Extracting archive
 - Upgrading sebastian/comparator (6.3.1 => 7.0.1): Extracting archive
 - Upgrading sebastian/cli-parser (3.0.2 => 4.0.0): Extracting archive
 - Upgrading phpunit/php-timer (7.0.1 => 8.0.0): Extracting archive
 - Upgrading phpunit/php-text-template (4.0.1 => 5.0.0): Extracting archive
 - Upgrading phpunit/php-invoker (5.0.1 => 6.0.0): Extracting archive
 - Upgrading phpunit/php-file-iterator (5.1.0 => 6.0.0): Extracting archive
 - Upgrading sebastian/lines-of-code (3.0.1 => 4.0.0): Extracting archive
 - Upgrading sebastian/complexity (4.0.1 => 5.0.0): Extracting archive
 - Upgrading phpunit/php-code-coverage (11.0.9 => 12.0.5): Extracting archive
 - Upgrading phpunit/phpunit (11.5.12 => 12.0.7): Extracting archive

i'll get the following results

 $  XDEBUG_MODE=coverage phpunit --testsuite=Application --coverage-text --filter=VuSmoke
PHPUnit 12.0.7 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.3.19 with Xdebug 3.4.1
Configuration: /home/easteregg/src/eos/tickeos/core/phpunit.dist.xml

.                                                                   1 / 1 (100%)

Time: 02:01.206, Memory: 1.81 GB

OK (1 test, 1 assertion)

@sebastianbergmann
Copy link
Owner

Also, once it's gone, re-running on PHPUnit 11 (which creates the cache anew) and then PHPUnit 12 without clearing the cache in between, re-introduces the issue.

With PHPUnit 11 and php-code-coverage < 11.0.9 you mean, I guess.

@verfriemelt-dot-org
Copy link

here is my phpunit config for completeness:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
        bootstrap="./tests/bootstrap.php"
        beStrictAboutOutputDuringTests="true"
        colors="true"
        defaultTestSuite="Legacy,Unit"
        cacheDirectory=".phpunit.cache"

        displayDetailsOnTestsThatTriggerErrors="true"
        displayDetailsOnPhpunitDeprecations="true"
        displayDetailsOnTestsThatTriggerDeprecations="true"
        displayDetailsOnIncompleteTests="true"
        displayDetailsOnTestsThatTriggerWarnings="true"

        failOnNotice="true"
        failOnWarning="true"
        failOnDeprecation="true"
>
    <extensions>
        <bootstrap class="Zenstruck\Foundry\PHPUnit\FoundryExtension"/>
    </extensions>
    <coverage />
    <testsuites>
        <testsuite name="Legacy">
            <directory>./tests/legacy</directory>
        </testsuite>
        <testsuite name="Application">
            <directory>./tests/application</directory>
        </testsuite>
        <testsuite name="Integration">
            <directory>./tests/integration</directory>
        </testsuite>
        <testsuite name="Unit">
            <directory>./tests/unit</directory>
        </testsuite>
        <testsuite name="phpstan">
            <directory>./.phpstan/tests</directory>
        </testsuite>
        <testsuite name="rector">
            <directory>./.rector/tests</directory>
        </testsuite>
    </testsuites>

    <php>
        <ini name="error_reporting" value="-1" />
        <ini name="memory_limit" value="-1" />
        <server name="SHELL_VERBOSITY" value="-1" />
        <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled=1" />
    </php>

    <source>
        <include>
            <directory>./apps</directory>
            <directory>./src</directory>
            <directory>./lib</directory>
            <directory>./plugins</directory>
        </include>
        <exclude>
            <directory>plugins/sfPropelPlugin/data/generator</directory>
        </exclude>
    </source>
</phpunit>

@sebastianbergmann
Copy link
Owner

@verfriemelt-dot-org That is indeed quite a slowdown. I have not observed something similar in any of my projects.

It would certainly help if you could determine which commit in php-code-coverage between 11.0 and main is responsible for this. As composer/composer#11119 has not been implemented yet, you will have to use approach explained by @greg0ire in https://dev.to/greg0ire/bisecting-vendors-12kd.

It would also be interesting to see where in the code this much more time is spent. This could be done using Xdebug's profiling functionality.

Please note that when comparing performance between PHPUnit 11 and PHPUnit 12 that in both cases the static analysis cache is warmed. It would be pointless to compare PHPUnit 11 with warmed static analysis cache to PHPUnit 12 without warmed static analysis cache. For example, you should try the following:

While on PHPUnit 11, run this:

❯ phpunit --warm-coverage-cache
❯ XDEBUG_MODE=coverage phpunit --testsuite=Application --coverage-text --filter=VuSmoke

Then update to PHPUnit 12 and run the above again.

@verfriemelt-dot-org
Copy link

i'll check everything out in a few hours and try to get a profile run.
as for the cache - i doubt that this is a cause here, since i tried both version with a fresh git clean & rerun both, with no difference between the runs of each version.

i'll update here as soon as i find something helpful

@verfriemelt-dot-org
Copy link

It would also be interesting to see where in the code this much more time is spent. This could be done using Xdebug's profiling functionality.

i sent you the link to two profile runs via the symfony slack :)

@sebastianbergmann
Copy link
Owner

I will have a look, but not today. Thank you.

@verfriemelt-dot-org
Copy link

bisecting from 11.0.8 to main, i ended up with this result:

 $  git bisect skip
There are only 'skip'ped commits left to test.
The first bad commit could be any of:
57d6d59d63925302a6651a3c7302cdb434867a31
9549d36facacc4175ae0068aae54fd3863aba3d6
c4ba7f2efacd4a432b5afc252f94da75ea6c754f
fc0626465f9b09df06808c28116e74827b4e556a
6c4fdfc90938c3597f56cf0fa9e70386592caab9
d6e6ecdaa5440ecf7d340d3689f000cdc5cab440
8e937acd3368c37e678a8892783896519e70005d
a5946cf0d5602b5f5a43bb6c5f0aded8433d3084
6eb6ce8050847a94e029daedf8c255cd42fee0fd
b0cefc5758a19ee8fbf4e92a69fb8d879494cae7
5349251d2f69abfd5db59e5663a736065e11b884
ba8e4efad70e1a79edb61e071ba30dfd7026c0c8
105fae8b605a04b21773ccf78c37ee5427849b25
e325d22dc5644183bd19c9b044af80adcc33edec
9f4fda1147804fa86d1fc31a0989abf74300c534
a481e7aae5215dc3eda0a9e61de8dda9cb92fe51
4e894fb355a8120e7bf3c9b369dfeefbf6700d0c
6fbbff5c78ee761945089ec2e12a77bfae261cda
5398005677eaa07487d99b0bf69976634e364d30
6fd234dc6b02b01042b2a6def49749aae4b20f21
9c5376c881c8d279a8216fd8e69f18f8ce414803
b25c2499faeb842766e295ab881e04360d526388
66d1136166a3709aecb5e704ab936ddbf2ce1535
873416b3e559601ade441f0f4ae210d7a13e2711
02adea855376b3289f7f7d23bfedc7e6f8948930
We cannot bisect more!

with many runs fail due the following error

Runtime:       PHP 8.3.19 with Xdebug 3.4.1
Configuration: /home/easteregg/src/eos/tickeos/core/phpunit.dist.xml



An error occurred inside PHPUnit.

Message:  Call to undefined method SebastianBergmann\CodeCoverage\CodeCoverage::validate()
Location: /home/easteregg/src/eos/tickeos/core/vendor/phpunit/phpunit/src/Runner/CodeCoverage.php:179

#0 /home/easteregg/src/eos/tickeos/core/vendor/phpunit/phpunit/src/Framework/TestRunner/TestRunner.php(159): PHPUnit\Runner\CodeCoverage->stop(true, Object(SebastianBergmann\CodeCoverage\Test\Target\TargetCollection), Object(SebastianBergmann\CodeCoverage\Test\Target\TargetCollection))
#1 /home/easteregg/src/eos/tickeos/core/vendor/phpunit/phpunit/src/Framework/TestCase.php(330): PHPUnit\Framework\TestRunner->run(Object(EosUptrade\TICKeos\Tests\application\Backend\Admin\AdminVuSmokeTest))
#2 /home/easteregg/src/eos/tickeos/core/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\Framework\TestCase->run()
#3 /home/easteregg/src/eos/tickeos/core/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\Framework\TestSuite->run()
#4 /home/easteregg/src/eos/tickeos/core/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\Framework\TestSuite->run()
#5 /home/easteregg/src/eos/tickeos/core/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(64): PHPUnit\Framework\TestSuite->run()
#6 /home/easteregg/src/eos/tickeos/core/vendor/phpunit/phpunit/src/TextUI/Application.php(210): PHPUnit\TextUI\TestRunner->run(Object(PHPUnit\TextUI\Configuration\Configuration), Object(PHPUnit\Runner\ResultCache\DefaultResultCache), Object(PHPUnit\Framework\TestSuite))
#7 /home/easteregg/src/eos/tickeos/core/vendor/phpunit/phpunit/phpunit(104): PHPUnit\TextUI\Application->run(Array)
#8 /home/easteregg/src/eos/tickeos/core/bin/phpunit(122): include('/home/easteregg...')
#9 {main}

@sebastianbergmann
Copy link
Owner

The revisions of phpunit/phpunit and phpunit/php-code-coverage have to match as there were API changes.

@verfriemelt-dot-org
Copy link

verfriemelt-dot-org commented Mar 16, 2025

i think i found the issue 🤔 maybe...

i dismissed the cache comment of yours for the most part, since we do not have a cache setup. (Cache for static analysis has not been configured)
after setting it up via cacheDirectory="var/phpunit" and warming it with phpunit --warm-coverage-cache (which took about 50s) the runtime with phpunit 12 is on par with phpunit 11.

thus the runtime difference comes imho mostly from the ParsingFileAnalyser class running with codecoverage enabled? 🤔 so this regression happens when run without the coverage-cache

@sebastianbergmann
Copy link
Owner

Performing static analysis while code coverage data collection is active should be avoided. Looks like I may have messed up while working towards PHPUnit 12. I will investigate, thank you for your feedback.

That being said, though: configuring a cache directory for PHPUnit cannot be recommended enough. It is, for instance, done in the best practice configuration file that can be generated using phpunit --generate-configuration.

@verfriemelt-dot-org
Copy link

that being said, i setup the cache as recommended and i got still a big memory increase + still timeouts in our pipeline due to extended runtimes. with simple unittests its not too big of a deal, which i used for testing here for the most part.

but for application tests touching lots of codepaths, it still is a siginificant runtime increase noticable, But so far i can say that our pipeline used to take about 10-15 minutes and now times out at the 60minutes mark when the run is terminated.

i'll collect some data, and append it here in a few hours

@staabm
Copy link
Contributor

staabm commented Mar 17, 2025

Could you create something small which reproduces the problem for us to work with?

@verfriemelt-dot-org
Copy link

here are the promised full runs to illustrate the current situation. this is the same codebase/configuration with cache enabled and warmed for 11.5 and 12.0

$ XDEBUG_MODE=coverage bin/phpunit \
    --testsuite=Application \
    --do-not-cache-result \
    --log-junit phpunit-report.xml \
    --coverage-cobertura phpunit-coverage.xml \
    --coverage-text --only-summary-for-coverage-text \
    --colors=never

PHPUnit 11.5.12 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.3.19 with Xdebug 3.4.1
Configuration: /home/easteregg/src/eos/tickeos/core/phpunit.dist.xml

...............................................................  63 / 117 ( 53%)
......................................................          117 / 117 (100%)

Time: 07:40.565, Memory: 164.29 MB

OK (117 tests, 1528 assertions)

Generating code coverage report in Cobertura XML format ... done [00:26.512]


Code Coverage Report Summary:
  Classes:  2.54% (279/10998)
  Methods:  6.71% (6181/92145)
  Lines:    6.41% (44906/700244)

vs 12

 $  XDEBUG_MODE=coverage bin/phpunit \
    --testsuite=Application \
    --do-not-cache-result \
    --log-junit phpunit-report.xml \
    --coverage-cobertura phpunit-coverage.xml \
    --coverage-text --only-summary-for-coverage-text \
    --colors=never

PHPUnit 12.0.7 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.3.19 with Xdebug 3.4.1
Configuration: /home/easteregg/src/eos/tickeos/core/phpunit.dist.xml

...............................................................  63 / 117 ( 53%)
......................................................          117 / 117 (100%)

Time: 03:54:55.614, Memory: 1.93 GB

OK (117 tests, 1528 assertions)

Generating code coverage report in Cobertura XML format ... done [00:33.992]


Code Coverage Report Summary:
  Classes:  2.54% (279/10998)
  Methods:  6.71% (6180/92145)
  Lines:    6.41% (44904/700244)

it seems the problem scales with the size of the codebase and the files analysed. i checked all of our already migrated smaller projects (about 27k loc) and observed there a rough doubling in runtime - from ~15-18 seconds with 11.5 vs ~30s with 12.0.

i did not noticed it before, because its more or less negligible. the project with the extreme difference here is about 1.7M lines of code 🤔

i have yet to find a public repository which illustrates this issue, but i think more or less every codebase sufficent in size will have this issue. also the downstream tooling like infections will suffer from this performance hit.

i keep my eyes open, if i find a public repo to showcase. if i can help you in any other way with this, let me know, you can also contact me on the sf-slack if you want to try something on our codebase :)

@jrfnl
Copy link
Contributor Author

jrfnl commented Mar 17, 2025

@verfriemelt-dot-org FYI: the repo where I originally noticed the issue is public and it's a relatively small codebase.

@sebastianbergmann
Copy link
Owner

@jrfnl How do I run the tests for yoast/phpunit-polyfills with PHPUnit 11 and how with PHPUnit 12? Is the first-party code configured in phpunit.xml (<source>) the same in both cases?

@jrfnl
Copy link
Contributor Author

jrfnl commented Mar 17, 2025

@sebastianbergmann There's composer scripts defined to make it easier:
https://github.com/Yoast/PHPUnit-Polyfills/blob/134921bfca9b02d8f374c48381451da1d98402f9/composer.json#L79-L90

Or when running with the PHAR file:

  • PHPUnit 11: phpunit -c phpunit11.xml.dist
  • PHPUnit 12: phpunit -c phpunit12.xml.dist

@sebastianbergmann
Copy link
Owner

I must be doing something completely wrong, or my Linux environment behaves very differently from your Windows environment with regards to these performance aspects, but I cannot reproduce a decrease in performance from PHPUnit 11.5 to PHPUnit 12.0 for the yoast/phpunit-polyfills test suite:

  • PHPUnit 11.5 with PCOV: Time: 00:00.322, Memory: 31.36 MB
  • PHPUnit 12.0 with PCOV: Time: 00:00.279, Memory: 29.30 MB
  • PHPUnit 11.5 with Xdebug: Time: 00:01.875, Memory: 48.73 MB
  • PHPUnit 12.0 with Xdebug: Time: 00:01.769, Memory: 48.60 MB

You can see below how I ran the test suite using PHPUnit 11.5 and PHPUnit 12.0.

PHPUnit 11.5 with PCOV

❯ rm -rf .cache
❯ php phpunit-11.phar --configuration phpunit11.xml.dist --warm-coverage-cache
PHPUnit 11.5.12 by Sebastian Bergmann and contributors.

Warming cache for static analysis ... [00:00.074]
❯ php phpunit-11.phar --configuration phpunit11.xml.dist
PHPUnit 11.5.12 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.5 with PCOV 1.0.12
Configuration: /home/sb/PHPUnit-Polyfills/phpunit11.xml.dist

...............................................................  63 / 471 ( 13%)
............................................................... 126 / 471 ( 26%)
.................SSSS....SSSS....SSSSSSSS...................... 189 / 471 ( 40%)
...........SSSSSSSSSSSS........................................ 252 / 471 ( 53%)
............................................................... 315 / 471 ( 66%)
......................................................S.....S.. 378 / 471 ( 80%)
............S.....S......SSS....SSS............................ 441 / 471 ( 93%)
..............................                                  471 / 471 (100%)

Time: 00:00.322, Memory: 31.36 MB

OK, but some tests were skipped!
Tests: 471, Assertions: 865, Skipped: 38.

Generating code coverage report in Clover XML format ... done [00:00.011]


Code Coverage Report Summary:
  Classes:  9.52% (2/21)     
  Methods:  5.88% (7/119)    
  Lines:    3.61% (16/443)   

PHPUnit 11.5 with Xdebug

❯ rm -rf .cache
❯ php phpunit-11.phar --configuration phpunit11.xml.dist --warm-coverage-cache
PHPUnit 12.0.7 by Sebastian Bergmann and contributors.

Warming cache for static analysis ... [00:00.076]
❯ XDEBUG_MODE=coverage php phpunit-11.phar --configuration phpunit11.xml.dist
PHPUnit 11.5.12 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.5 with Xdebug 3.4.2
Configuration: /home/sb/PHPUnit-Polyfills/phpunit11.xml.dist

...............................................................  63 / 471 ( 13%)
............................................................... 126 / 471 ( 26%)
.................SSSS....SSSS....SSSSSSSS...................... 189 / 471 ( 40%)
...........SSSSSSSSSSSS........................................ 252 / 471 ( 53%)
............................................................... 315 / 471 ( 66%)
......................................................S.....S.. 378 / 471 ( 80%)
............S.....S......SSS....SSS............................ 441 / 471 ( 93%)
..............................                                  471 / 471 (100%)

Time: 00:01.875, Memory: 48.73 MB

OK, but some tests were skipped!
Tests: 471, Assertions: 865, Skipped: 38.

Generating code coverage report in Clover XML format ... done [00:00.170]


Code Coverage Report Summary:
  Classes:  9.52% (2/21)     
  Methods:  5.88% (7/119)    
  Lines:    3.61% (16/443)   

PHPUnit 12.0 with PCOV

❯ rm -rf .cache
❯ php phpunit-12.phar --configuration phpunit12.xml.dist --warm-coverage-cache
PHPUnit 12.0.7 by Sebastian Bergmann and contributors.

Warming cache for static analysis ... [00:00.076]
❯ php phpunit-12.phar --configuration phpunit12.xml.dist
PHPUnit 12.0.7 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.5 with PCOV 1.0.12
Configuration: /home/sb/PHPUnit-Polyfills/phpunit12.xml.dist

...............................................................  63 / 443 ( 14%)
............................................................... 126 / 443 ( 28%)
............................................................... 189 / 443 ( 42%)
............................................................... 252 / 443 ( 56%)
............................................................... 315 / 443 ( 71%)
..........................S.....S..............S.....S......SSS 378 / 443 ( 85%)
....SSS........................................................ 441 / 443 ( 99%)
..                                                              443 / 443 (100%)

Time: 00:00.279, Memory: 29.30 MB

OK, but some tests were skipped!
Tests: 443, Assertions: 865, Skipped: 10.

Generating code coverage report in Clover XML format ... done [00:00.006]


Code Coverage Report Summary:
  Classes:  9.52% (2/21)     
  Methods:  5.88% (7/119)    
  Lines:    3.61% (16/443)   

PHPUnit 11.5 with Xdebug

❯ rm -rf .cache
❯ php phpunit-12.phar --configuration phpunit12.xml.dist --warm-coverage-cache
PHPUnit 12.0.7 by Sebastian Bergmann and contributors.

Warming cache for static analysis ... [00:00.076]
❯ XDEBUG_MODE=coverage php phpunit-12.phar --configuration phpunit12.xml.dist
PHPUnit 12.0.7 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.5 with Xdebug 3.4.2
Configuration: /home/sb/PHPUnit-Polyfills/phpunit12.xml.dist

...............................................................  63 / 443 ( 14%)
............................................................... 126 / 443 ( 28%)
............................................................... 189 / 443 ( 42%)
............................................................... 252 / 443 ( 56%)
............................................................... 315 / 443 ( 71%)
..........................S.....S..............S.....S......SSS 378 / 443 ( 85%)
....SSS........................................................ 441 / 443 ( 99%)
..                                                              443 / 443 (100%)

Time: 00:01.769, Memory: 48.60 MB

OK, but some tests were skipped!
Tests: 443, Assertions: 865, Skipped: 10.

Generating code coverage report in Clover XML format ... done [00:00.012]


Code Coverage Report Summary:
  Classes:  9.52% (2/21)     
  Methods:  5.88% (7/119)    
  Lines:    3.61% (16/443)   

@verfriemelt-dot-org
Copy link

verfriemelt-dot-org commented Mar 17, 2025

i cant reproduce it with PHPUnit-Polyfills either (tried php8.3 and 8.4, running linux)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants