Files
michaelschiemer/tests/Performance/PerformanceTimingComparisonTest.php
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

205 lines
8.0 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Performance;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Performance\PerformanceCollector;
use PHPUnit\Framework\TestCase;
/**
* Performance comparison tests between hrtime() and microtime() implementations
*/
final class PerformanceTimingComparisonTest extends TestCase
{
private SystemClock $clock;
private SystemHighResolutionClock $highResClock;
private PerformanceCollector $microtimeCollector;
private EnhancedPerformanceCollector $hrtimeCollector;
protected function setUp(): void
{
$this->clock = new SystemClock();
$this->highResClock = new SystemHighResolutionClock($this->clock);
$this->microtimeCollector = new PerformanceCollector(true);
$this->hrtimeCollector = new EnhancedPerformanceCollector($this->clock, $this->highResClock);
}
public function testHrtimePrecisionVsMicrotime(): void
{
$iterations = 1000;
// Test a very fast operation multiple times
$fastOperation = fn () => array_sum([1, 2, 3, 4, 5]);
// Benchmark with hrtime()
$hrtimeResults = $this->hrtimeCollector->benchmark('hrtime_test', $fastOperation, $iterations);
// Benchmark with microtime() - simulate multiple measurements
$microtimeDurations = [];
for ($i = 0; $i < $iterations; $i++) {
$start = microtime(true);
$fastOperation();
$end = microtime(true);
$microtimeDurations[] = Duration::fromSeconds($end - $start);
}
// Calculate microtime statistics
$microtimeNanos = array_map(fn (Duration $d) => $d->toNanoseconds(), $microtimeDurations);
$microtimeAvg = Duration::fromNanoseconds((int) (array_sum($microtimeNanos) / count($microtimeNanos)));
// Assert precision differences
$this->assertInstanceOf(Duration::class, $hrtimeResults['average']);
$this->assertInstanceOf(Duration::class, $hrtimeResults['min']);
$this->assertInstanceOf(Duration::class, $hrtimeResults['max']);
// hrtime() should provide more consistent results (lower variance)
$hrtimeVariance = $this->calculateDurationVariance([
$hrtimeResults['min'],
$hrtimeResults['average'],
$hrtimeResults['max'],
]);
$microtimeVariance = $this->calculateDurationVariance($microtimeDurations);
// Output comparison results
echo "\n=== Performance Timing Comparison ===\n";
echo "Iterations: {$iterations}\n";
echo "hrtime() Average: " . $hrtimeResults['average']->toMicroseconds() . " μs\n";
echo "microtime() Average: " . $microtimeAvg->toMicroseconds() . " μs\n";
echo "hrtime() Min: " . $hrtimeResults['min']->toMicroseconds() . " μs\n";
echo "hrtime() Max: " . $hrtimeResults['max']->toMicroseconds() . " μs\n";
echo "hrtime() Variance: {$hrtimeVariance} ns²\n";
echo "microtime() Variance: {$microtimeVariance} ns²\n";
// Validate that both systems work
$this->assertGreaterThan(0, $hrtimeResults['average']->toNanoseconds());
$this->assertGreaterThan(0, $microtimeAvg->toNanoseconds());
// hrtime() should generally be more precise for very fast operations
// Note: Result is only returned for single iteration benchmarks
if ($iterations === 1) {
$this->assertNotNull($hrtimeResults['result'], 'hrtime benchmark should return result');
}
}
public function testNanosecondPrecisionMeasurement(): void
{
// Test that we can measure very small durations with nanosecond precision
$veryFastOp = fn () => 1 + 1;
$result = $this->hrtimeCollector->measureDuration($veryFastOp);
$this->assertArrayHasKey('result', $result);
$this->assertArrayHasKey('duration', $result);
$this->assertInstanceOf(Duration::class, $result['duration']);
$this->assertEquals(2, $result['result']);
// Should be able to measure durations in nanoseconds
$nanoseconds = $result['duration']->toNanoseconds();
$this->assertIsInt($nanoseconds);
$this->assertGreaterThanOrEqual(0, $nanoseconds);
echo "Very fast operation duration: {$nanoseconds} ns\n";
}
public function testCollectorAccuracyComparison(): void
{
$operation = function () {
// Simulate some work
for ($i = 0; $i < 1000; $i++) {
$x = sqrt($i);
}
return $x;
};
// Measure with both collectors
$this->microtimeCollector->startTiming('test', PerformanceCategory::BENCHMARK);
$result1 = $operation();
$this->microtimeCollector->endTiming('test');
$enhancedResult = $this->hrtimeCollector->measure('test_enhanced', PerformanceCategory::BENCHMARK, $operation);
// Get measurements
$microtimeMetric = $this->microtimeCollector->getMetric('test');
$hrtimeMetric = $this->hrtimeCollector->getMetric('test_enhanced');
$this->assertNotNull($microtimeMetric);
$this->assertNotNull($hrtimeMetric);
$microtimeDuration = $microtimeMetric->getAverageDuration();
$hrtimeDuration = $hrtimeMetric->getAverageDuration();
// Both should measure similar durations for the same operation
$this->assertInstanceOf(Duration::class, $microtimeDuration);
$this->assertInstanceOf(Duration::class, $hrtimeDuration);
// Results should be the same
$this->assertEquals($result1, $enhancedResult);
echo "Microtime duration: " . $microtimeDuration->toMicroseconds() . " μs\n";
echo "hrtime duration: " . $hrtimeDuration->toMicroseconds() . " μs\n";
}
public function testHighResolutionClockFeatures(): void
{
// Test the additional features of the high-resolution clock
$startTime = $this->hrtimeCollector->getHighResTime();
usleep(1000); // 1ms sleep
$endTime = $this->hrtimeCollector->getHighResTime();
$this->assertIsInt($startTime);
$this->assertIsInt($endTime);
$this->assertGreaterThan($startTime, $endTime);
// Test duration creation
$duration = $this->hrtimeCollector->createDurationFromNanoseconds($endTime - $startTime);
$this->assertInstanceOf(Duration::class, $duration);
// Should be approximately 1ms (allowing for system variation)
$microseconds = $duration->toMicroseconds();
$this->assertGreaterThan(800, $microseconds); // At least 0.8ms
$this->assertLessThan(2000, $microseconds); // Less than 2ms
echo "Sleep measurement: {$microseconds} μs (expected ~1000 μs)\n";
}
public function testRequestDurationAccuracy(): void
{
// Test total request duration measurement
$requestStart = $this->hrtimeCollector->getTotalRequestDuration();
$this->assertInstanceOf(Duration::class, $requestStart);
// Should have some duration since collector was created
$this->assertGreaterThan(0, $requestStart->toMicroseconds());
usleep(500); // 0.5ms
$requestAfterSleep = $this->hrtimeCollector->getTotalRequestDuration();
$this->assertGreaterThan($requestStart->toMicroseconds(), $requestAfterSleep->toMicroseconds());
echo "Request duration before sleep: " . $requestStart->toMicroseconds() . " μs\n";
echo "Request duration after sleep: " . $requestAfterSleep->toMicroseconds() . " μs\n";
}
private function calculateDurationVariance(array $durations): float
{
$nanoseconds = array_map(fn (Duration $d) => $d->toNanoseconds(), $durations);
$mean = array_sum($nanoseconds) / count($nanoseconds);
$squaredDiffs = array_map(fn ($value) => ($value - $mean) ** 2, $nanoseconds);
return array_sum($squaredDiffs) / count($squaredDiffs);
}
}