diff --git a/packages/class-files-iterator/composer.json b/packages/class-files-iterator/composer.json index 11735a8d0..2f0f8a0e8 100644 --- a/packages/class-files-iterator/composer.json +++ b/packages/class-files-iterator/composer.json @@ -24,6 +24,7 @@ "ext-json": "*" }, "require-dev": { - "phpunit/phpunit": "^10.5.38" + "phpunit/phpunit": "^10.5.38", + "symfony/finder": "^7.4" } } diff --git a/packages/class-files-iterator/tests/src/PerformanceTest.php b/packages/class-files-iterator/tests/src/PerformanceTest.php new file mode 100644 index 000000000..7b1bdfba2 --- /dev/null +++ b/packages/class-files-iterator/tests/src/PerformanceTest.php @@ -0,0 +1,72 @@ +requireParentN(1); + $dir = $nsdir->getDirectory(); + $tick = $this->stopwatch(); + $dts = []; + + for ($i = 0; $i < 10; ++$i) { + $it = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS); + $itt = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST); + $itt_files = iterator_to_array($itt); + $dts['itt'][] = $tick(); + + $finder = Finder::create()->files()->name('*.php')->in($dir); + $n_finder = iterator_count($finder); + $dts['finder'][] = $tick(); + + $n_nsdir = iterator_count($nsdir); + $dts['nsdir'][] = $tick(); + } + + $itt_php_files = preg_grep('#\.php$#', $itt_files); + assert($itt_php_files !== false); + $n_itt = count($itt_php_files); + $this->assertGreaterThan(300, $n_itt); + $this->assertLessThan(3000, $n_itt); + $this->assertSame($n_itt, $n_finder); + $this->assertSame($n_itt, $n_nsdir); + + $dt_counts = array_map(count(...), $dts); + $this->assertSame([], array_diff($dt_counts, [10])); + + // For a deterministic operation, the fastest time is usually the most + // reproducible and comparable. + // @phpstan-ignore argument.type + $dt_mins = array_map(min(...), $dts); + + $dt_ref = $dt_mins['itt']; + $this->assertEqualsWithDeltaFactor(5.4, $dt_mins['finder'] / $dt_ref, .1); + $this->assertEqualsWithDeltaFactor(3.17, $dt_mins['nsdir'] / $dt_ref, .1); + } + + protected function assertEqualsWithDeltaFactor(float $expected, float $actual, float $delta): void { + $this->assertLessThan($expected * (1 + $delta), $actual); + $this->assertGreaterThan($expected / (1 + $delta), $actual); + } + + /** + * Starts a stopwatch timer. + * + * @return \Closure(): float + * Function which returns milliseconds since previous call. + */ + protected function stopwatch(): \Closure { + $t0 = hrtime(true); + return function () use (&$t0) { + $dt = hrtime(true) - $t0; + $t0 += $dt; + return $dt * .000001; + }; + } + +}