Skip to content

Commit 03ae62f

Browse files
authored
Merge pull request #11216 from vimeo/scan_threads
Add support for --scan-threads CLI and config flag
2 parents a17fdd7 + b9c449a commit 03ae62f

20 files changed

+112
-83
lines changed

config.xsd

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
<xs:attribute name="disableSuppressAll" type="xs:boolean" default="false" />
8282
<xs:attribute name="triggerErrorExits" type="TriggerErrorExitsType" default="default" />
8383
<xs:attribute name="threads" type="xs:integer" />
84+
<xs:attribute name="scanThreads" type="xs:integer" />
8485
<xs:anyAttribute processContents="skip" />
8586
</xs:complexType>
8687

docs/running_psalm/configuration.md

+8
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,14 @@ Allows you to hard-code a compressor for Psalm's cache. By default, Psalm uses `
438438
```
439439
Allows you to hard-code the number of threads Psalm will use (similar to `--threads` on the command line). This value will be used in place of detecting threads from the host machine, but will be overridden by using `--threads` or `--debug` (which sets threads to 1) on the command line
440440

441+
#### scanThreads
442+
```xml
443+
<psalm
444+
scanThreads="[int]"
445+
>
446+
```
447+
Allows you to hard-code the number of threads Psalm will use during the scan phase (as opposed to the analysis phase, controlled by the `threads` field) (similar to `--scan-threads` on the command line). This value will be used in place of detecting threads from the host machine, but will be overridden by using `--scan-threads` or `--debug` (which sets threads to 1) on the command line.
448+
441449
#### maxStringLength
442450
```xml
443451
<psalm

psalm-baseline.xml

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<files psalm-version="dev-master@e9e270f363bd05ea3790890de3f04f6726ea5643">
2+
<files psalm-version="6.x-dev@d3c7ca5430b3a75d8fab6439af1eefe119d12512">
33
<file src="examples/TemplateChecker.php">
44
<PossiblyUndefinedIntArrayOffset>
55
<code><![CDATA[$comment_block->tags['variablesfrom'][0]]]></code>
@@ -1106,7 +1106,6 @@
11061106
<code><![CDATA[!$paths_to_check]]></code>
11071107
<code><![CDATA[$baseline_file_path]]></code>
11081108
<code><![CDATA[$cache_directory]]></code>
1109-
<code><![CDATA[$config->threads]]></code>
11101109
<code><![CDATA[$find_references_to]]></code>
11111110
<code><![CDATA[empty($baselineFile)]]></code>
11121111
<code><![CDATA[getenv('PSALM_SHEPHERD')]]></code>
@@ -2006,6 +2005,9 @@
20062005
</RiskyTruthyFalsyComparison>
20072006
</file>
20082007
<file src="src/Psalm/IssueBuffer.php">
2008+
<PossiblyUnusedMethod>
2009+
<code><![CDATA[getServer]]></code>
2010+
</PossiblyUnusedMethod>
20092011
<RiskyTruthyFalsyComparison>
20102012
<code><![CDATA[!$report_options->output_path]]></code>
20112013
<code><![CDATA[$parent_issue_type]]></code>

src/Psalm/Config.php

+19-4
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
use function libxml_clear_errors;
8787
use function libxml_get_errors;
8888
use function libxml_use_internal_errors;
89+
use function max;
8990
use function mkdir;
9091
use function phpversion;
9192
use function preg_match;
@@ -463,7 +464,10 @@ final class Config
463464
*/
464465
public array $internal_stubs = [];
465466

467+
/** @var ?int<1, max> */
466468
public ?int $threads = null;
469+
/** @var ?int<1, max> */
470+
public ?int $scan_threads = null;
467471

468472
/**
469473
* A list of php extensions supported by Psalm.
@@ -1379,12 +1383,25 @@ private static function fromXmlAndPaths(
13791383
}
13801384

13811385
if (isset($config_xml['threads'])) {
1382-
$config->threads = (int)$config_xml['threads'];
1386+
$config->threads = max(1, (int)$config_xml['threads']);
1387+
$config->scan_threads = $config->threads;
1388+
}
1389+
1390+
if (isset($config_xml['scanThreads'])) {
1391+
$config->scan_threads = max(1, (int)$config_xml['scanThreads']);
13831392
}
13841393

13851394
return $config;
13861395
}
13871396

1397+
/**
1398+
* @psalm-suppress PossiblyUnusedMethod, PropertyTypeCoercion
1399+
* @internal */
1400+
public static function setInstance(self $config): void
1401+
{
1402+
self::$instance = $config;
1403+
}
1404+
13881405
public static function getInstance(): Config
13891406
{
13901407
if (self::$instance) {
@@ -2428,9 +2445,7 @@ public function visitComposerAutoloadFiles(ProjectAnalyzer $project_analyzer, ?P
24282445
// as they might be autoloadable once we require the autoloader below
24292446
$codebase->classlikes->forgetMissingClassLikes();
24302447

2431-
$this->include_collector->runAndCollect(
2432-
$this->requireAutoloader(...),
2433-
);
2448+
$this->include_collector->runAndCollect($this->requireAutoloader(...));
24342449
}
24352450

24362451
$this->collectPredefinedConstants();

src/Psalm/Internal/Analyzer/ProjectAnalyzer.php

+6-40
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
namespace Psalm\Internal\Analyzer;
66

7-
use Fidry\CpuCoreCounter\CpuCoreCounter;
8-
use Fidry\CpuCoreCounter\NumberOfCpuCoreNotFound;
97
use InvalidArgumentException;
108
use Psalm\Codebase;
119
use Psalm\Config;
@@ -65,11 +63,9 @@
6563
use function array_shift;
6664
use function clearstatcache;
6765
use function count;
68-
use function defined;
6966
use function dirname;
7067
use function end;
7168
use function explode;
72-
use function extension_loaded;
7369
use function file_exists;
7470
use function fwrite;
7571
use function implode;
@@ -204,6 +200,7 @@ public function __construct(
204200
public ?ReportOptions $stdout_report_options = null,
205201
public array $generated_report_options = [],
206202
public int $threads = 1,
203+
public int $scanThreads = 1,
207204
?Progress $progress = null,
208205
?Codebase $codebase = null,
209206
) {
@@ -364,15 +361,6 @@ public function serverMode(LanguageServer $server): void
364361
$this->file_reference_provider->loadReferenceCache();
365362
$this->codebase->enterServerMode();
366363

367-
$cpu_count = self::getCpuCount();
368-
369-
// let's not go crazy
370-
$usable_cpus = $cpu_count - 2;
371-
372-
if ($usable_cpus > 1) {
373-
$this->threads = $usable_cpus;
374-
}
375-
376364
$server->logInfo("Initializing: Initialize Plugins...");
377365
$this->config->initializePlugins($this);
378366

@@ -484,7 +472,7 @@ public function check(string $base_dir, bool $is_diff = false): void
484472

485473
$this->config->initializePlugins($this);
486474

487-
$this->codebase->scanFiles($this->threads);
475+
$this->codebase->scanFiles($this->scanThreads);
488476

489477
$this->codebase->infer_types_from_usage = true;
490478
} else {
@@ -508,7 +496,7 @@ public function check(string $base_dir, bool $is_diff = false): void
508496

509497
$this->config->initializePlugins($this);
510498

511-
$this->codebase->scanFiles($this->threads);
499+
$this->codebase->scanFiles($this->scanThreads);
512500
} else {
513501
$diff_no_files = true;
514502
}
@@ -885,7 +873,7 @@ public function checkDir(string $dir_name): void
885873

886874
$this->config->initializePlugins($this);
887875

888-
$this->codebase->scanFiles($this->threads);
876+
$this->codebase->scanFiles($this->scanThreads);
889877

890878
$this->config->visitStubFiles($this->codebase, $this->progress);
891879

@@ -995,7 +983,7 @@ public function checkFile(string $file_path): void
995983

996984
$this->config->initializePlugins($this);
997985

998-
$this->codebase->scanFiles($this->threads);
986+
$this->codebase->scanFiles($this->scanThreads);
999987

1000988
$this->config->visitStubFiles($this->codebase, $this->progress);
1001989

@@ -1039,7 +1027,7 @@ public function checkPaths(array $paths_to_check): void
10391027
$this->config->initializePlugins($this);
10401028

10411029

1042-
$this->codebase->scanFiles($this->threads);
1030+
$this->codebase->scanFiles($this->scanThreads);
10431031

10441032
$this->config->visitStubFiles($this->codebase, $this->progress);
10451033

@@ -1311,28 +1299,6 @@ public function getFunctionLikeAnalyzer(
13111299
return $function_analyzer;
13121300
}
13131301

1314-
/**
1315-
* Adapted from https://gist.github.com/divinity76/01ef9ca99c111565a72d3a8a6e42f7fb
1316-
* returns number of cpu cores
1317-
* Copyleft 2018, license: WTFPL
1318-
*
1319-
* @throws NumberOfCpuCoreNotFound
1320-
*/
1321-
public static function getCpuCount(): int
1322-
{
1323-
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
1324-
// No support desired for Windows at the moment
1325-
return 1;
1326-
}
1327-
1328-
if (!extension_loaded('pcntl')) {
1329-
// Psalm requires pcntl for multi-threads support
1330-
return 1;
1331-
}
1332-
1333-
return (new CpuCoreCounter())->getCount();
1334-
}
1335-
13361302
/**
13371303
* @return array<int, string>
13381304
* @psalm-pure

src/Psalm/Internal/Cli/Psalm.php

+38-19
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Psalm\Internal\Cli;
66

77
use Composer\Autoload\ClassLoader;
8+
use Fidry\CpuCoreCounter\CpuCoreCounter;
89
use Psalm\Config;
910
use Psalm\Config\Creator;
1011
use Psalm\ErrorBaseline;
@@ -146,6 +147,7 @@ final class Psalm
146147
'show-snippet:',
147148
'stats',
148149
'threads:',
150+
'scan-threads:',
149151
'update-baseline',
150152
'use-baseline:',
151153
'use-ini-defaults',
@@ -271,11 +273,12 @@ public static function run(array $argv): void
271273
$options['long-progress'] = true;
272274
}
273275

274-
$threads = self::detectThreads($options, $config, $in_ci);
276+
$threads = self::getThreads($options, $config, $in_ci, false);
277+
$scanThreads = self::getThreads($options, $config, $in_ci, true);
275278

276279
$progress = self::initProgress($options, $config, $in_ci);
277280

278-
self::restart($options, $threads, $progress);
281+
self::restart($options, $threads, $scanThreads, $progress);
279282

280283
if (isset($options['debug-emitted-issues'])) {
281284
$config->debug_emitted_issues = true;
@@ -353,6 +356,7 @@ public static function run(array $argv): void
353356
: true,
354357
),
355358
$threads,
359+
$scanThreads,
356360
$progress,
357361
);
358362

@@ -412,6 +416,32 @@ public static function run(array $argv): void
412416
}
413417
}
414418

419+
public static function getThreads(array $options, Config $config, bool $in_ci, bool $for_scan): int
420+
{
421+
if ($for_scan) {
422+
if (isset($options['scanThreads'])) {
423+
$threads = (int)$options['scanThreads'];
424+
} elseif (isset($options['debug']) || $in_ci) {
425+
$threads = 1;
426+
} elseif ($config->scan_threads) {
427+
$threads = $config->scan_threads;
428+
} else {
429+
$threads = max(1, (new CpuCoreCounter())->getCount());
430+
}
431+
} else {
432+
if (isset($options['threads'])) {
433+
$threads = (int)$options['threads'];
434+
} elseif (isset($options['debug']) || $in_ci) {
435+
$threads = 1;
436+
} elseif ($config->threads) {
437+
$threads = $config->threads;
438+
} else {
439+
$threads = max(1, (new CpuCoreCounter())->getCount());
440+
}
441+
}
442+
return $threads;
443+
}
444+
415445
private static function initOutputFormat(array $options): string
416446
{
417447
return isset($options['output-format']) && is_string($options['output-format'])
@@ -880,7 +910,7 @@ private static function getCurrentDir(array $options): string
880910
return $current_dir;
881911
}
882912

883-
private static function restart(array $options, int $threads, Progress $progress): void
913+
private static function restart(array $options, int $threads, int $scanThreads, Progress $progress): void
884914
{
885915
$ini_handler = new PsalmRestarter('PSALM');
886916

@@ -897,7 +927,7 @@ private static function restart(array $options, int $threads, Progress $progress
897927
}
898928
}
899929

900-
if ($threads > 1
930+
if (($threads > 1 || $scanThreads > 1)
901931
&& extension_loaded('grpc')
902932
&& (ini_get('grpc.enable_fork_support') === '1' && ini_get('grpc.poll_strategy') === 'epoll1') === false
903933
) {
@@ -957,20 +987,6 @@ private static function restart(array $options, int $threads, Progress $progress
957987
}
958988
}
959989

960-
private static function detectThreads(array $options, Config $config, bool $in_ci): int
961-
{
962-
if (isset($options['threads'])) {
963-
$threads = (int)$options['threads'];
964-
} elseif (isset($options['debug']) || $in_ci) {
965-
$threads = 1;
966-
} elseif ($config->threads) {
967-
$threads = $config->threads;
968-
} else {
969-
$threads = max(1, ProjectAnalyzer::getCpuCount() - 1);
970-
}
971-
return $threads;
972-
}
973-
974990
/** @psalm-suppress UnusedParam $argv is being reported as unused */
975991
private static function forwardCliCall(array $options, array $argv): void
976992
{
@@ -1303,7 +1319,10 @@ private static function getHelpText(): string
13031319
If set, requires JIT acceleration to be available in order to run Psalm, exiting immediately if it cannot be enabled.
13041320
13051321
--threads=INT
1306-
If greater than one, Psalm will run analysis on multiple threads, speeding things up.
1322+
If greater than one, Psalm will run the scan and analysis on multiple threads, speeding things up.
1323+
1324+
--scan-threads=INT
1325+
If greater than one, Psalm will run the scan on multiple threads, speeding things up (if specified, takes priority over the --threads flag).
13071326
13081327
--no-diff
13091328
Turns off Psalm’s diff mode, checks all files regardless of whether they’ve changed.

src/Psalm/Internal/Cli/Psalter.php

+6-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
use function in_array;
4949
use function is_array;
5050
use function is_dir;
51-
use function is_numeric;
5251
use function is_string;
5352
use function microtime;
5453
use function pathinfo;
@@ -87,7 +86,7 @@ final class Psalter
8786
private const LONG_OPTIONS = [
8887
'help', 'debug', 'debug-by-line', 'debug-emitted-issues', 'config:', 'file:', 'root:',
8988
'plugin:', 'issues:', 'list-supported-issues', 'php-version:', 'dry-run', 'safe-types',
90-
'find-unused-code', 'threads:', 'codeowner:',
89+
'find-unused-code', 'threads:', 'scan-threads:', 'codeowner:',
9190
'allow-backwards-incompatible-changes:',
9291
'add-newline-between-docblock-annotations:',
9392
'no-cache',
@@ -263,8 +262,11 @@ public static function run(array $argv): void
263262
$current_dir = $config->base_dir;
264263
chdir($current_dir);
265264
}
265+
266+
$in_ci = CliUtils::runningInCI();
266267

267-
$threads = isset($options['threads']) && is_numeric($options['threads']) ? (int)$options['threads'] : 1;
268+
$threads = Psalm::getThreads($options, $config, $in_ci, false);
269+
$scanThreads = Psalm::getThreads($options, $config, $in_ci, true);
268270

269271
if (isset($options['no-cache'])) {
270272
$providers = new Providers(
@@ -304,6 +306,7 @@ public static function run(array $argv): void
304306
$stdout_report_options,
305307
[],
306308
$threads,
309+
$scanThreads,
307310
$progress,
308311
);
309312

0 commit comments

Comments
 (0)