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
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"composer-runtime-api": "^2.0",
"clue/ndjson-react": "^1.0",
"composer/semver": "^3.4",
"fidry/cpu-core-counter": "^1.2",
"fidry/cpu-core-counter": "^1.4",
"guzzlehttp/guzzle": "^7.9",
"hoa/compiler": "3.17.08.08",
"hoa/exception": "^1.0",
Expand Down
17 changes: 9 additions & 8 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 6 additions & 15 deletions src/Diagnose/SystemResourcesDiagnoseExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

namespace PHPStan\Diagnose;

use Fidry\CpuCoreCounter\Diagnoser;
use PHPStan\Command\Output;
use PHPStan\DependencyInjection\AutowiredParameter;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Process\CpuCoreCounter;
use PHPStan\Process\SystemResources;
use function sprintf;

/**
Expand All @@ -19,7 +19,6 @@ final class SystemResourcesDiagnoseExtension implements DiagnoseExtension

public function __construct(
private CpuCoreCounter $cpuCoreCounter,
private SystemResources $systemResources,
#[AutowiredParameter(ref: '%parallel.loadLimit%')]
private ?float $loadLimit,
)
Expand All @@ -32,21 +31,13 @@ public function print(Output $output): void
$output->writeLineFormatted(sprintf('Detected CPU cores: %d', $this->cpuCoreCounter->getDetectedNumberOfCpuCores()));
$output->writeLineFormatted(sprintf('Load limit: %s', $this->loadLimit === null ? 'none' : (string) $this->loadLimit));

$kubernetesLimit = $this->cpuCoreCounter->getKubernetesCpuLimit();
$output->writeLineFormatted(sprintf(
'KUBERNETES_CPU_LIMIT: %s',
$kubernetesLimit === null ? 'none' : sprintf('%d cores', $kubernetesLimit),
));
$output->writeLineFormatted(sprintf('Available after limits: %d', $this->cpuCoreCounter->getNumberOfCpuCoresAfterLimits()));

$quota = $this->systemResources->getCpuQuota();
$output->writeLineFormatted(sprintf(
'cgroup CPU quota: %s',
$quota === null ? 'none' : sprintf('%d cores', $quota),
));

$output->writeLineFormatted(sprintf('Usable CPU cores: %d', $this->cpuCoreCounter->getNumberOfCpuCores()));
$output->writeLineFormatted('');

// lists every limit the counter looks at, also one that is overridden by the limit found before it
$output->writeLineFormatted('<info>CPU core limit:</info>');
$output->writeRaw(Diagnoser::diagnose([$this->cpuCoreCounter->getCountLimitFinder()]));
$output->writeLineFormatted('');
Comment on lines +37 to +40

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested the output with this PR:

➜  phpstan-src git:(pr/6620) php bin/phpstan diagnose -vvv           
Note: Using configuration file /Users/m.staab/dvl/phpstan-src/phpstan.neon.dist.
PHP runtime version: 8.5.11
PHP composer.json required version: 8.2-8.6.99
PHP version for analysis: 8.5.11 (from config)

PHPStan version: 2.2.x-dev@1571cfc
PHPStan running from:
bin/phpstan

Extension installer: Not installed

Included configs from Composer packages:
phpstan/phpstan-deprecation-rules (rules.neon): 2.0.4
phpstan/phpstan-nette (extension.neon): 2.0.11
phpstan/phpstan-nette (rules.neon): 2.0.11
phpstan/phpstan-phpunit (extension.neon): 2.0.x-dev
phpstan/phpstan-phpunit (rules.neon): 2.0.x-dev
phpstan/phpstan-strict-rules (rules.neon): 2.0.10
shipmonk/dead-code-detector (rules.neon): 1.0.0

Discovered Composer project root:
/Users/m.staab/dvl/phpstan-src

Parallel worker creation:
Mechanism:                 fork (pcntl_fork)

Turbo extension: not loaded
Turbo platform: macos-arm64 (os: Darwin, machine: arm64, libc: gnu, php: 8.5, zts: no, debug: no)
Turbo worker binary: none found
Turbo trusted types: off (extension inactive)

System resources:
Detected CPU cores:        14
Load limit:                1
cgroup CPU quota:          none
KUBERNETES_CPU_LIMIT:      none
Usable CPU cores:          14

I think it looks good

@staabm staabm Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comparing this PR diagnose with master I see "Avaiable after limits" is no longer available, which I think is expected, correct @AJenbo ?

grafik

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm merging this. Feel free to send a follow-up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that’s expected. The relevant cap is applied internally to the lib so we can't really show the this info. Not a problem I think.

}

}
63 changes: 13 additions & 50 deletions src/Process/CpuCoreCounter.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,88 +3,51 @@
namespace PHPStan\Process;

use Fidry\CpuCoreCounter\CpuCoreCounter as FidryCpuCoreCounter;
use Fidry\CpuCoreCounter\NumberOfCpuCoreNotFound;
use Fidry\CpuCoreCounter\Finder\CpuCoreFinder;
use Fidry\CpuCoreCounter\Finder\FinderRegistry;
use Fidry\CpuCoreCounter\ParallelisationResult;
use PHPStan\DependencyInjection\AutowiredParameter;
use PHPStan\DependencyInjection\AutowiredService;
use function min;

#[AutowiredService]
final class CpuCoreCounter
{

private ?int $count = null;
private ?ParallelisationResult $result = null;

/**
* What fidry/cpu-core-counter found: the raw count, the count left after the
* load limit and KUBERNETES_CPU_LIMIT were applied, and that environment limit.
*
* @var array{int, int, int|null}|null
*/
private ?array $detected = null;
private CpuCoreFinder $countLimitFinder;

public function __construct(
#[AutowiredParameter(ref: '%parallel.loadLimit%')]
private ?float $loadLimit,
private SystemResources $systemResources,
)
{
$this->countLimitFinder = FinderRegistry::getDefaultCountLimitFinder();
}

/**
* Cores PHPStan may actually use: what the machine reports, reduced by the load
* limit and capped by the CPU quota of the cgroup it runs in.
* limit and capped by the cgroup CPU quota or, failing that, KUBERNETES_CPU_LIMIT.
*/
public function getNumberOfCpuCores(): int
{
if ($this->count !== null) {
return $this->count;
}

$count = $this->getNumberOfCpuCoresAfterLimits();

// fidry/cpu-core-counter has no cgroup finder, and its nproc-based default
// honours a cpuset affinity mask but not a CFS bandwidth quota, so inside a
// `docker run --cpus=2` container it reports the host's core count
$quota = $this->systemResources->getCpuQuota();
if ($quota !== null) {
$count = min($count, $quota);
}

return $this->count = $count;
return $this->detect()->availableCpus;
}

/** What the machine reports, before any limit is applied. */
public function getDetectedNumberOfCpuCores(): int
{
return $this->detect()[0];
return $this->detect()->totalCoresCount;
}

/** The detected count after the load limit and KUBERNETES_CPU_LIMIT, before any cgroup quota. */
public function getNumberOfCpuCoresAfterLimits(): int
public function getCountLimitFinder(): CpuCoreFinder
{
return $this->detect()[1];
return $this->countLimitFinder;
}

public function getKubernetesCpuLimit(): ?int
private function detect(): ParallelisationResult
{
return $this->detect()[2];
}

/** @return array{int, int, int|null} */
private function detect(): array
{
if ($this->detected !== null) {
return $this->detected;
}

try {
$result = (new FidryCpuCoreCounter())->getAvailableForParallelisation(0, null, $this->loadLimit);
$this->detected = [$result->totalCoresCount, $result->availableCpus, $result->correctedCountLimit];
} catch (NumberOfCpuCoreNotFound) {
$this->detected = [1, 1, null];
}

return $this->detected;
return $this->result ??= (new FidryCpuCoreCounter(countLimitFinder: $this->countLimitFinder))->getAvailableForParallelisation(0, null, $this->loadLimit);
}

}
Loading
Loading