Skip to content
Open
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 docs/running_psalm/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ When `true`, Psalm will attempt to find all unused code (including unused variab
forceJit="[bool]"
>
```
When `true`, Psalm will exit immediately if JIT acceleration (up to +20% performance) cannot be enabled, the equivalent of running with `--force-jit`. Defaults to `false`.
When `true`, Psalm will enable JIT acceleration and exit immediately if it cannot be enabled, the equivalent of running with `--force-jit`. When `false` (default), Psalm runs without JIT.

#### noCache
```xml
Expand Down
11 changes: 6 additions & 5 deletions src/Psalm/Internal/Cli/Psalm.php
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,7 @@ private static function restart(
Progress $progress,
): void {
$ini_handler = new PsalmRestarter('PSALM');
$ini_handler->enableJit = $force_jit;

if (isset($options['disable-extension'])) {
if (is_array($options['disable-extension'])) {
Expand Down Expand Up @@ -1001,20 +1002,20 @@ private static function restart(
$progress->write(PHP_EOL
. 'JIT acceleration: ON'
. PHP_EOL . PHP_EOL);
} else {
} elseif ($force_jit) {
$progress->write(PHP_EOL
. 'JIT acceleration: OFF (an error occurred while enabling JIT)' . PHP_EOL
. 'Please report this to https://github.com/vimeo/psalm with your OS and PHP configuration!'
. PHP_EOL . PHP_EOL);
}
} else {
} elseif ($force_jit) {
$progress->write(PHP_EOL
. 'JIT acceleration: OFF (opcache not installed or not enabled)' . PHP_EOL
. 'Install and enable the opcache extension to make use of JIT for a 20%+ performance boost!'
. 'Install and enable the opcache extension to use JIT with --force-jit.'
. PHP_EOL . PHP_EOL);
}
if ($force_jit && !$hasJit) {
$progress->write('Exiting because JIT was requested but is not available.' . PHP_EOL . PHP_EOL);
$progress->write('Exiting because --force-jit was set but JIT is not available.' . PHP_EOL . PHP_EOL);
exit(1);
}

Expand Down Expand Up @@ -1393,7 +1394,7 @@ private static function getHelpText(): string
Used to disable certain extensions while Psalm is running.

--force-jit
If set, requires JIT acceleration to be available in order to run Psalm, exiting immediately if it cannot be enabled.
Enable JIT acceleration. Exits immediately if JIT cannot be enabled.

--threads=INT
If greater than one, Psalm will run the scan and analysis on multiple threads, speeding things up.
Expand Down
47 changes: 31 additions & 16 deletions src/Psalm/Internal/Fork/PsalmRestarter.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,20 @@ final class PsalmRestarter extends XdebugHandler
private const REQUIRED_OPCACHE_SETTINGS = [
'enable' => 1,
'enable_cli' => 1,
'jit' => 1205,
'validate_timestamps' => 0,
'file_update_protection' => 0,
'jit_buffer_size' => 128 * 1024 * 1024,
'max_accelerated_files' => 1_000_000,
'interned_strings_buffer' => 64,
'optimization_level' => '0x7FFEBFFF',
'preload' => '',
'log_verbosity_level' => 0,
'save_comments' => 1,
'restrict_api' => '',
];

private const JIT_OPCACHE_SETTINGS = [
'jit' => 1205,
'jit_buffer_size' => 128 * 1024 * 1024,
'jit_max_root_traces' => 100_000,
'jit_max_side_traces' => 100_000,
'jit_max_exit_counters' => 100_000,
Expand All @@ -45,13 +53,10 @@ final class PsalmRestarter extends XdebugHandler
'jit_hot_side_exit' => 1,
'jit_blacklist_root_trace' => 255,
'jit_blacklist_side_trace' => 255,
'optimization_level' => '0x7FFEBFFF',
'preload' => '',
'log_verbosity_level' => 0,
'save_comments' => 1,
'restrict_api' => '',
];

public bool $enableJit = false;

private bool $required = false;

/**
Expand Down Expand Up @@ -90,8 +95,7 @@ protected function requiresRestart($default): bool
return true;
}

// restart to enable JIT if it's not configured in the optimal way
foreach (self::REQUIRED_OPCACHE_SETTINGS as $ini_name => $required_value) {
foreach ($this->getEffectiveOpcacheSettings() as $ini_name => $required_value) {
$value = (string) ini_get("opcache.$ini_name");
if ($ini_name === 'jit_buffer_size') {
$value = self::toBytes($value);
Expand All @@ -105,7 +109,7 @@ protected function requiresRestart($default): bool
}
}

$requiredMemoryConsumption = self::getRequiredMemoryConsumption();
$requiredMemoryConsumption = $this->getRequiredMemoryConsumption();

if ((int)ini_get('opcache.memory_consumption') < $requiredMemoryConsumption) {
return true;
Expand Down Expand Up @@ -169,17 +173,16 @@ protected function restart($command): void
// if it wasn't loaded then we apparently don't have opcache installed and there's no point trying
// to tweak it
$additional_options = $opcache_loaded ? [] : ['-dzend_extension=opcache'];
foreach (self::REQUIRED_OPCACHE_SETTINGS as $key => $value) {
foreach ($this->getEffectiveOpcacheSettings() as $key => $value) {
$additional_options []= "-dopcache.{$key}={$value}";
}

$requiredMemoryConsumption = self::getRequiredMemoryConsumption();
$requiredMemoryConsumption = $this->getRequiredMemoryConsumption();

if ((int)ini_get('opcache.memory_consumption') < $requiredMemoryConsumption) {
$additional_options []= "-dopcache.memory_consumption={$requiredMemoryConsumption}";
}


array_splice(
$command,
1,
Expand All @@ -191,16 +194,28 @@ protected function restart($command): void
parent::restart($command);
}

/**
* @return array<string, int|string>
*/
private function getEffectiveOpcacheSettings(): array
{
if ($this->enableJit) {
return self::REQUIRED_OPCACHE_SETTINGS + self::JIT_OPCACHE_SETTINGS;
}

return self::REQUIRED_OPCACHE_SETTINGS;
}

/**
* @return positive-int
*/
private static function getRequiredMemoryConsumption(): int
private function getRequiredMemoryConsumption(): int
{
// Reserve for byte-codes
$result = 256;

if (isset(self::REQUIRED_OPCACHE_SETTINGS['jit_buffer_size'])) {
$result += self::REQUIRED_OPCACHE_SETTINGS['jit_buffer_size'] / 1024 / 1024;
if ($this->enableJit) {
$result += self::JIT_OPCACHE_SETTINGS['jit_buffer_size'] / 1024 / 1024;
}

if (isset(self::REQUIRED_OPCACHE_SETTINGS['interned_strings_buffer'])) {
Expand Down