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
11 changes: 10 additions & 1 deletion src/Parallel/Scheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ public function scheduleWork(
// large and small files - chunking a sorted list would concentrate the
// heaviest files into a single job and create one long-running straggler
$fileSizes = [];
foreach ($files as $file) {
$originalOrder = [];
foreach ($files as $i => $file) {
$fileSizes[$file] = $fileSizeCallback($file);
$originalOrder[$file] = $i;
}
usort($files, static fn (string $a, string $b): int => $fileSizes[$b] <=> $fileSizes[$a]);

Expand All @@ -63,6 +65,13 @@ public function scheduleWork(
$stripedJobs[$i % $numberOfJobs][] = $file;
}

// only the job composition should change, not the order in which files
// of a job get analysed - analysis results can be sensitive to it
foreach ($stripedJobs as &$stripedJob) {
usort($stripedJob, static fn (string $a, string $b): int => $originalOrder[$a] <=> $originalOrder[$b]);
}
unset($stripedJob);

$jobs = array_values($stripedJobs);
$numberOfProcesses = min(
max((int) floor(count($jobs) / $this->minimumNumberOfJobsPerProcess), 1),
Expand Down
25 changes: 21 additions & 4 deletions tests/PHPStan/Parallel/SchedulerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,31 @@ public function testHeaviestFilesAreSpreadAcrossJobs(): void
$schedule = $scheduler->scheduleWork(16, array_keys($fileSizes), static fn (string $file): int => $fileSizes[$file] ?? 0);

// six files, job size 2 -> three jobs; the three heaviest files must not
// share a job, and every job pairs one heavy file with one light file
// share a job, and every job pairs one heavy file with one light file,
// listed in input order
$this->assertSame([
['f.php', 'c.php'],
['e.php', 'b.php'],
['d.php', 'a.php'],
['c.php', 'f.php'],
['b.php', 'e.php'],
['a.php', 'd.php'],
], $schedule->getJobs());
}

public function testFilesWithinAJobKeepTheirInputOrder(): void
{
// a small file followed by a larger one - the size sort must only
// influence which job a file lands in, never the analysis order
// inside the job (analysis results can be sensitive to it)
$fileSizes = [
'bootstrap.php' => 327,
'src/Middleware.php' => 560,
];

$scheduler = new Scheduler(20, 16, 1);
$schedule = $scheduler->scheduleWork(16, array_keys($fileSizes), static fn (string $file): int => $fileSizes[$file] ?? 0);

$this->assertSame([['bootstrap.php', 'src/Middleware.php']], $schedule->getJobs());
}

public function testEveryFileIsScheduledExactlyOnce(): void
{
$files = [];
Expand Down
Loading