Skip to content

Commit

Permalink
Auto-detecting number of CPUs/processes (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
SmetDenis committed Jun 9, 2022
1 parent e986bc1 commit 34446cb
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 9 deletions.
52 changes: 52 additions & 0 deletions src/Cli.php
Expand Up @@ -26,6 +26,7 @@
use Symfony\Component\Console\Output\OutputInterface;

use function JBZoo\Utils\bool;
use function JBZoo\Utils\int;

/**
* Class CliHelper
Expand Down Expand Up @@ -75,6 +76,8 @@ class Cli
*/
private int $prevMemory;

private ?int $numberOfCpuCores = null;

/**
* @param InputInterface $input
* @param OutputInterface $output
Expand Down Expand Up @@ -384,4 +387,53 @@ public function isProgressBarDisabled(): bool
{
return bool($this->getInput()->getOption('no-progress'));
}

/**
* @return int
* @see https://github.com/phpstan/phpstan-src/blob/f8be122188/src/Process/CpuCoreCounter.php
*/
public function getNumberOfCpuCores(): int
{
if ($this->numberOfCpuCores !== null) {
return $this->numberOfCpuCores;
}

if (!\function_exists('proc_open')) {
return $this->numberOfCpuCores = 1;
}

// from brianium/paratest
// Linux (and potentially Windows with linux sub systems)
if (\is_file('/proc/cpuinfo')) {
$cpuinfo = \file_get_contents('/proc/cpuinfo');
if ($cpuinfo !== false) {
\preg_match_all('/^processor/m', $cpuinfo, $matches);
return $this->numberOfCpuCores = \count($matches[0]);
}
}

// Windows
if (\DIRECTORY_SEPARATOR === '\\') {
$process = \popen('wmic cpu get NumberOfLogicalProcessors', 'rb');
if (\is_resource($process)) {
/** @phan-suppress-next-line PhanPluginUseReturnValueInternalKnown */
\fgets($process);
$cores = int(\fgets($process));
\pclose($process);

return $this->numberOfCpuCores = $cores;
}
}

// *nix (Linux, BSD and Mac)
$process = \popen('sysctl -n hw.ncpu', 'rb');
if (\is_resource($process)) {
$cores = int(\fgets($process));
\pclose($process);

return $this->numberOfCpuCores = $cores;
}

return $this->numberOfCpuCores = 2;
}
}
41 changes: 35 additions & 6 deletions src/CliCommandMultiProc.php
Expand Up @@ -24,16 +24,17 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Process\Process;

use function JBZoo\Utils\int;

/**
* Class CliCommandMultiProc
* @package JBZoo\Cli
*/
abstract class CliCommandMultiProc extends CliCommand
{
private const PM_DEFAULT_MAX_PROCESSES = 20;
private const PM_DEFAULT_INTERVAL = 100;
private const PM_DEFAULT_START_DELAY = 1;
private const PM_DEFAULT_TIMEOUT = 7200;
private const PM_DEFAULT_INTERVAL = 100;
private const PM_DEFAULT_START_DELAY = 1;
private const PM_DEFAULT_TIMEOUT = 7200;

/**
* @var array
Expand All @@ -56,7 +57,7 @@ protected function configure(): void
null,
InputOption::VALUE_REQUIRED,
'Process Manager. The number of processes to execute in parallel (os isolated processes)',
self::PM_DEFAULT_MAX_PROCESSES
'auto'
)
->addOption(
'pm-interval',
Expand Down Expand Up @@ -137,8 +138,19 @@ protected function executeAction(): int
*/
protected function executeMultiProcessAction(): int
{
$procNum = $this->getNumberOfProcesses();
$cpuCores = $this->helper->getNumberOfCpuCores();
$this->_("Max number of sub-processes: {$procNum}", OutLvl::DEBUG);
if ($procNum > $cpuCores) {
$this->_(
"The specified number of processes (--pm-max={$procNum}) "
. "is more than the found number of CPU cores in the system ({$cpuCores}).",
OutLvl::WARNING
);
}

$procManager = $this->initProcManager(
$this->getOptInt('pm-max') ?: self::PM_DEFAULT_MAX_PROCESSES,
$procNum,
$this->getOptInt('pm-interval') ?: self::PM_DEFAULT_INTERVAL,
$this->getOptInt('pm-start-delay') ?: self::PM_DEFAULT_START_DELAY
);
Expand Down Expand Up @@ -343,4 +355,21 @@ private function getMaxTimeout(): int
{
return $this->getOptInt('pm-max-timeout') ?: self::PM_DEFAULT_TIMEOUT;
}

/**
* @return int
*/
private function getNumberOfProcesses(): int
{
$pmMax = \strtolower($this->getOptString('pm-max'));
$cpuCores = $this->helper->getNumberOfCpuCores();

if ($pmMax === 'auto') {
return $cpuCores;
}

$pmMaxInt = \abs(int($pmMax));

return $pmMaxInt > 0 ? $pmMaxInt : $cpuCores;
}
}
24 changes: 21 additions & 3 deletions tests/Cli/CliMultiProcessTest.php
Expand Up @@ -30,7 +30,7 @@ public function testAsRealExecution()
$start = microtime(true);
$result = Helper::executeReal(
'test:sleep-multi 123 " qwerty "',
['sleep' => 1, 'no-progress' => null],
['sleep' => 1, 'no-progress' => null, 'pm-max' => 50],
'JBZOO_TEST_VAR=123456'
);

Expand Down Expand Up @@ -61,7 +61,7 @@ public function testAsRealExecution()
public function testAsVirtalExecution()
{
$start = microtime(true);
$result = Helper::executeVirtaul('test:sleep-multi', ['sleep' => 1, 'no-progress' => null]);
$result = Helper::executeVirtaul('test:sleep-multi', ['sleep' => 1, 'no-progress' => null, 'pm-max' => 5]);
$time = microtime(true) - $start;

$outputAsArray = json($result)->getArrayCopy();
Expand All @@ -88,7 +88,10 @@ public function testAsVirtalExecution()
public function testException()
{
$start = microtime(true);
$result = Helper::executeReal('test:sleep-multi 123 456 789', ['sleep' => 2, 'no-progress' => null]);
$result = Helper::executeReal(
'test:sleep-multi 123 456 789',
['sleep' => 2, 'no-progress' => null, 'pm-max' => 5]
);
$time = microtime(true) - $start;

$outputAsArray = json($result[1])->getArrayCopy();
Expand All @@ -112,4 +115,19 @@ public function testException()

isTrue($time < 5, "Total time: {$time}");
}

public function testNumberOfCpuCores()
{
$result = Helper::executeReal(
'test:sleep-multi 123 " qwerty "',
['sleep' => 1, 'no-progress' => null, 'pm-max' => 50, '-vvv' => null]
);

isContain('Debug: Max number of sub-processes: 50', $result[1]);
isContain(
'Warning: The specified number of processes (--pm-max=50) '
. 'is more than the found number of CPU cores in the system',
$result[1]
);
}
}

0 comments on commit 34446cb

Please sign in to comment.