From 1b9e556a86c544ab8dc45abfb5f6b787fc5655f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Thu, 1 Aug 2024 22:15:18 +0200 Subject: [PATCH 1/6] feat: Adjust the load limit --- src/CpuCoreCounter.php | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/CpuCoreCounter.php b/src/CpuCoreCounter.php index 3fe3182..fc2ce87 100644 --- a/src/CpuCoreCounter.php +++ b/src/CpuCoreCounter.php @@ -43,6 +43,7 @@ public function __construct(?array $finders = null) } /** +<<<<<<< Updated upstream * @param positive-int|0 $reservedCpus Number of CPUs to reserve. This is useful when you want * to reserve some CPUs for other processes. If the main * process is going to be busy still, you may want to set @@ -55,16 +56,27 @@ public function __construct(?array $finders = null) * retrieved using `sys_getloadavg()` to check the load * of the system in the past minute. Should be a positive * float. +======= + * @param positive-int|0 $reservedCpus + * @param positive-int $limit + * @param float $loadLimit Element of [0., 1.]. Limits the number of CPUs based on the system load + * average. Set it to null or 1. to disable the check, it otherwise + * will adjust the number of CPUs based on the system load average. For example if 3 cores out of 10 are busy and the load limit is set to 50%, only 2 cores will be available for parallelisation. + * @param float $systemLoadAverage The system load average. If not provided, it will be + * retrieved using `sys_getloadavg()` to check the load + * of the system in the past minute. Should be a positive + * float. +>>>>>>> Stashed changes * * @see https://php.net/manual/en/function.sys-getloadavg.php */ public function getAvailableForParallelisation( int $reservedCpus = 0, ?int $limit = null, - ?float $loadLimitPerCore = .9, + ?float $loadLimit = .9, ?float $systemLoadAverage = null ): ParallelisationResult { - self::checkLoadLimitPerCore($loadLimitPerCore); + self::checkLoadLimitPerCore($loadLimit); self::checkSystemLoadAverage($systemLoadAverage); $correctedLimit = null === $limit @@ -81,7 +93,7 @@ public function getAvailableForParallelisation( $systemLoadAveragePerCore = $correctedSystemLoadAverage / $availableCpus; // Adjust available CPUs based on current load - if (null !== $loadLimitPerCore && $systemLoadAveragePerCore > $loadLimitPerCore) { + if (null !== $loadLimit && $systemLoadAveragePerCore > $loadLimit) { $adjustedCpus = max( 1, (1 - $systemLoadAveragePerCore) * $availableCpus @@ -96,7 +108,7 @@ public function getAvailableForParallelisation( return new ParallelisationResult( $reservedCpus, $limit, - $loadLimitPerCore, + $loadLimit, $systemLoadAverage, $correctedLimit, $correctedSystemLoadAverage, From a0365af8e492caab62307d754078945ba08bd11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Fri, 2 Aug 2024 10:52:20 +0200 Subject: [PATCH 2/6] WIP --- src/CpuCoreCounter.php | 76 ++++++++++++++++------------------- src/ParallelisationResult.php | 2 +- tests/CpuCoreCounterTest.php | 22 ++++++++-- 3 files changed, 54 insertions(+), 46 deletions(-) diff --git a/src/CpuCoreCounter.php b/src/CpuCoreCounter.php index fc2ce87..de25eba 100644 --- a/src/CpuCoreCounter.php +++ b/src/CpuCoreCounter.php @@ -43,77 +43,71 @@ public function __construct(?array $finders = null) } /** -<<<<<<< Updated upstream - * @param positive-int|0 $reservedCpus Number of CPUs to reserve. This is useful when you want - * to reserve some CPUs for other processes. If the main - * process is going to be busy still, you may want to set - * this value to 1. - * - * @param positive-int $limit - * @param float $loadLimitPerCore Limits the number of CPUs based on the system load - * average per core in a range of [0., 1.]. - * @param float $systemLoadAverage The system load average. If not provided, it will be - * retrieved using `sys_getloadavg()` to check the load - * of the system in the past minute. Should be a positive - * float. -======= - * @param positive-int|0 $reservedCpus - * @param positive-int $limit - * @param float $loadLimit Element of [0., 1.]. Limits the number of CPUs based on the system load - * average. Set it to null or 1. to disable the check, it otherwise - * will adjust the number of CPUs based on the system load average. For example if 3 cores out of 10 are busy and the load limit is set to 50%, only 2 cores will be available for parallelisation. - * @param float $systemLoadAverage The system load average. If not provided, it will be + * @param positive-int|0 $reservedCpus Number of CPUs to reserve. This is useful when you want + * to reserve some CPUs for other processes. If the main + * process is going to be busy still, you may want to set + * this value to 1. + * @param float|null $systemLoadLimit Element of [0., 1.]. Limits the number of CPUs based + * on the system load average. Set it to null or 1. to + * disable the check, it otherwise will adjust the number + * of CPUs based on the system load average. For example + * if 3 cores out of 10 are busy and the load limit is + * set to 50%, only 2 cores will be available for + * parallelisation. The reserved cores are also taken + * into consideration, i.e. if .7 is passed, it will + * consider the max system load limit should not be + * higher than 70% for the system cores minus the reserved + * ones. + * @param float $systemLoadAverage The system load average. If not provided, it will be * retrieved using `sys_getloadavg()` to check the load * of the system in the past minute. Should be a positive * float. ->>>>>>> Stashed changes * * @see https://php.net/manual/en/function.sys-getloadavg.php */ public function getAvailableForParallelisation( int $reservedCpus = 0, ?int $limit = null, - ?float $loadLimit = .9, + ?float $systemLoadLimit = .9, ?float $systemLoadAverage = null ): ParallelisationResult { - self::checkLoadLimitPerCore($loadLimit); + self::checkLoadLimitPerCore($systemLoadLimit); self::checkSystemLoadAverage($systemLoadAverage); $correctedLimit = null === $limit ? self::getKubernetesLimit() : $limit; - $totalCoresCount = $this->getCountWithFallback(1); - - $availableCpus = max(1, $totalCoresCount - $reservedCpus); - - $correctedSystemLoadAverage = null === $systemLoadAverage - ? sys_getloadavg()[0] ?? 0. - : $systemLoadAverage; - $systemLoadAveragePerCore = $correctedSystemLoadAverage / $availableCpus; + $totalCoreCount = $this->getCountWithFallback(1); + $availableCores = max(1, $totalCoreCount - $reservedCpus); // Adjust available CPUs based on current load - if (null !== $loadLimit && $systemLoadAveragePerCore > $loadLimit) { - $adjustedCpus = max( + if (null !== $systemLoadLimit) { + $correctedSystemLoadAverage = null === $systemLoadAverage + ? sys_getloadavg()[0] ?? 0. + : $systemLoadAverage; + + $numberOfFreeCores = max( 1, - (1 - $systemLoadAveragePerCore) * $availableCpus + $systemLoadLimit * $totalCoreCount - $correctedSystemLoadAverage ); - $availableCpus = min($availableCpus, $adjustedCpus); + + $availableCores = min($availableCores, $numberOfFreeCores); } - if (null !== $correctedLimit && $availableCpus > $correctedLimit) { - $availableCpus = $correctedLimit; + if (null !== $correctedLimit && $availableCores > $correctedLimit) { + $availableCores = $correctedLimit; } return new ParallelisationResult( $reservedCpus, $limit, - $loadLimit, + $systemLoadLimit, $systemLoadAverage, $correctedLimit, - $correctedSystemLoadAverage, - $totalCoresCount, - (int) $availableCpus + $correctedSystemLoadAverage ?? $systemLoadAverage, + $totalCoreCount, + (int) $availableCores ); } diff --git a/src/ParallelisationResult.php b/src/ParallelisationResult.php index 1db52dc..eb3b404 100644 --- a/src/ParallelisationResult.php +++ b/src/ParallelisationResult.php @@ -70,7 +70,7 @@ public function __construct( ?float $passedLoadLimitPerCore, ?float $passedSystemLoadAverage, ?int $correctedLimit, - float $correctedSystemLoadAverage, + ?float $correctedSystemLoadAverage, int $totalCoresCount, int $availableCpus ) { diff --git a/tests/CpuCoreCounterTest.php b/tests/CpuCoreCounterTest.php index 97aa82c..bfe92d3 100644 --- a/tests/CpuCoreCounterTest.php +++ b/tests/CpuCoreCounterTest.php @@ -307,10 +307,10 @@ public static function availableCpuCoreProvider(): iterable return [ [$finder], [], + 0, null, null, - null, - null, + .0, 5, ]; })(); @@ -367,7 +367,7 @@ public static function availableCpuCoreProvider(): iterable null, .9, 6., - 10, + 3, ]; })(); @@ -386,7 +386,7 @@ public static function availableCpuCoreProvider(): iterable })(); yield 'CPU count found, the CPUs are being the limit set, but there is several CPUs available still' => (static function () { - $finder = new DummyCpuCoreFinder(11); + $finder = new DummyCpuCoreFinder(10); return [ [$finder], @@ -426,6 +426,20 @@ public static function availableCpuCoreProvider(): iterable 10, ]; })(); + + yield 'load limit doc example' => (static function () { + $finder = new DummyCpuCoreFinder(10); + + return [ + [$finder], + [], + 0, + null, + .5, + 3, + 2, + ]; + })(); } /** From f0ed9f74033aef4fdfa943cb7dc19d7cdc47edd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Fri, 2 Aug 2024 12:01:36 +0200 Subject: [PATCH 3/6] rework --- src/CpuCoreCounter.php | 40 ++++++++++++++--------------- tests/AvailableCpuCoresScenario.php | 8 +++--- tests/CpuCoreCounterTest.php | 26 +++++++++++++------ 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/CpuCoreCounter.php b/src/CpuCoreCounter.php index de25eba..46d10a7 100644 --- a/src/CpuCoreCounter.php +++ b/src/CpuCoreCounter.php @@ -47,18 +47,18 @@ public function __construct(?array $finders = null) * to reserve some CPUs for other processes. If the main * process is going to be busy still, you may want to set * this value to 1. - * @param float|null $systemLoadLimit Element of [0., 1.]. Limits the number of CPUs based - * on the system load average. Set it to null or 1. to - * disable the check, it otherwise will adjust the number - * of CPUs based on the system load average. For example - * if 3 cores out of 10 are busy and the load limit is - * set to 50%, only 2 cores will be available for - * parallelisation. The reserved cores are also taken - * into consideration, i.e. if .7 is passed, it will - * consider the max system load limit should not be - * higher than 70% for the system cores minus the reserved - * ones. - * @param float $systemLoadAverage The system load average. If not provided, it will be + * @param float|null $loadLimit Element of [0., 1.]. Percentage representing the + * amount of cores that should be used among the available + * resources. For instance, if set to 0.7, it will use 70% + * of the available cores, i.e. if 1 core is reserved, 11 + * cores are available and 5 are busy, it will use 70% + * of (11-1-5)=5 cores, so 3 cores. Set this parameter to null + * to skip this check. Beware that 1 does not mean "no limit", + * but 100% of the _available_ resources, i.e. with the + * previous example, it will return 5 cores. How busy is + * the system is determined by the system load average + * (see $systemLoadAverage). + * @param float $systemLoadAverage The system load average. If not provided, it will be * retrieved using `sys_getloadavg()` to check the load * of the system in the past minute. Should be a positive * float. @@ -68,10 +68,10 @@ public function __construct(?array $finders = null) public function getAvailableForParallelisation( int $reservedCpus = 0, ?int $limit = null, - ?float $systemLoadLimit = .9, + ?float $loadLimit = null, ?float $systemLoadAverage = null ): ParallelisationResult { - self::checkLoadLimitPerCore($systemLoadLimit); + self::checkLoadLimit($loadLimit); self::checkSystemLoadAverage($systemLoadAverage); $correctedLimit = null === $limit @@ -82,17 +82,15 @@ public function getAvailableForParallelisation( $availableCores = max(1, $totalCoreCount - $reservedCpus); // Adjust available CPUs based on current load - if (null !== $systemLoadLimit) { + if (null !== $loadLimit) { $correctedSystemLoadAverage = null === $systemLoadAverage ? sys_getloadavg()[0] ?? 0. : $systemLoadAverage; - $numberOfFreeCores = max( + $availableCores = max( 1, - $systemLoadLimit * $totalCoreCount - $correctedSystemLoadAverage + $loadLimit * ($availableCores - $correctedSystemLoadAverage) ); - - $availableCores = min($availableCores, $numberOfFreeCores); } if (null !== $correctedLimit && $availableCores > $correctedLimit) { @@ -102,7 +100,7 @@ public function getAvailableForParallelisation( return new ParallelisationResult( $reservedCpus, $limit, - $systemLoadLimit, + $loadLimit, $systemLoadAverage, $correctedLimit, $correctedSystemLoadAverage ?? $systemLoadAverage, @@ -211,7 +209,7 @@ public static function getKubernetesLimit(): ?int return $finder->find(); } - private static function checkLoadLimitPerCore(?float $loadLimitPerCore): void + private static function checkLoadLimit(?float $loadLimitPerCore): void { if (null === $loadLimitPerCore) { return; diff --git a/tests/AvailableCpuCoresScenario.php b/tests/AvailableCpuCoresScenario.php index 1a5ef55..ba2309c 100644 --- a/tests/AvailableCpuCoresScenario.php +++ b/tests/AvailableCpuCoresScenario.php @@ -49,7 +49,7 @@ public function __construct( array $environmentVariables, int $reservedCpus, ?int $limit, - ?float $loadLimitPerCore, + ?float $loadLimit, ?float $systemLoadAverage, int $expected ) { @@ -57,7 +57,7 @@ public function __construct( $this->environmentVariables = $environmentVariables; $this->reservedCpus = $reservedCpus; $this->limit = $limit; - $this->loadLimitPerCore = $loadLimitPerCore; + $this->loadLimitPerCore = $loadLimit; $this->systemLoadAverage = $systemLoadAverage; $this->expected = $expected; } @@ -76,7 +76,7 @@ public static function create( array $environmentVariables, ?int $reservedCpus, ?int $limit, - ?float $loadLimitPerCore, + ?float $loadLimit, ?float $systemLoadAverage, int $expected ): array { @@ -90,7 +90,7 @@ public static function create( $environmentVariables, $reservedCpus ?? 0, $limit, - $loadLimitPerCore, + $loadLimit, $systemLoadAverage ?? 0., $expected ), diff --git a/tests/CpuCoreCounterTest.php b/tests/CpuCoreCounterTest.php index 5eb7ba4..8211dc8 100644 --- a/tests/CpuCoreCounterTest.php +++ b/tests/CpuCoreCounterTest.php @@ -299,16 +299,26 @@ public static function availableCpuCoreProvider(): iterable 1 ); - yield 'CPU count found, over half the cores are used' => AvailableCpuCoresScenario::create( + yield 'CPU count found, over half the cores are used and no limit is set' => AvailableCpuCoresScenario::create( 11, [], 1, null, - .9, + null, 6., 10 ); + yield 'CPU count found, over half the cores are used and a limit is set' => AvailableCpuCoresScenario::create( + 11, + [], + 1, + null, + 1., + 6., + 4 + ); + yield 'CPU count found, the CPUs are overloaded' => AvailableCpuCoresScenario::create( 11, [], @@ -319,24 +329,24 @@ public static function availableCpuCoreProvider(): iterable 1 ); - yield 'CPU count found, the CPUs are being the limit set, but there is several CPUs available still' => AvailableCpuCoresScenario::create( + yield 'CPU count found, the load limit is set, but there is several CPUs available still' => AvailableCpuCoresScenario::create( 11, [], 1, null, .5, 6., - 4 + 2 ); - yield 'CPU count found, the CPUs are at the limit of being overloaded' => AvailableCpuCoresScenario::create( + yield 'CPU count found, the CPUs are at completely overloaded' => AvailableCpuCoresScenario::create( 11, [], 1, null, - .9, - 9., - 10 + .5, + 11., + 1 ); yield 'CPU count found, the CPUs are overloaded but no load limit per CPU' => AvailableCpuCoresScenario::create( From c80af3a4e5f2814cb99efa97f0f918b4617e6f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Fri, 2 Aug 2024 12:06:46 +0200 Subject: [PATCH 4/6] fix --- src/CpuCoreCounter.php | 3 ++- src/ParallelisationResult.php | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/CpuCoreCounter.php b/src/CpuCoreCounter.php index 46d10a7..a41532c 100644 --- a/src/CpuCoreCounter.php +++ b/src/CpuCoreCounter.php @@ -47,6 +47,7 @@ public function __construct(?array $finders = null) * to reserve some CPUs for other processes. If the main * process is going to be busy still, you may want to set * this value to 1. + * @param positive-int $limit * @param float|null $loadLimit Element of [0., 1.]. Percentage representing the * amount of cores that should be used among the available * resources. For instance, if set to 0.7, it will use 70% @@ -58,7 +59,7 @@ public function __construct(?array $finders = null) * previous example, it will return 5 cores. How busy is * the system is determined by the system load average * (see $systemLoadAverage). - * @param float $systemLoadAverage The system load average. If not provided, it will be + * @param float|null $systemLoadAverage The system load average. If not provided, it will be * retrieved using `sys_getloadavg()` to check the load * of the system in the past minute. Should be a positive * float. diff --git a/src/ParallelisationResult.php b/src/ParallelisationResult.php index eb3b404..36e12c5 100644 --- a/src/ParallelisationResult.php +++ b/src/ParallelisationResult.php @@ -31,7 +31,7 @@ final class ParallelisationResult /** * @var float|null */ - public $passedLoadLimitPerCore; + public $passedLoadLimit; /** * @var float|null @@ -44,7 +44,7 @@ final class ParallelisationResult public $correctedLimit; /** - * @var float + * @var float|null */ public $correctedSystemLoadAverage; @@ -67,7 +67,7 @@ final class ParallelisationResult public function __construct( int $passedReservedCpus, ?int $passedLimit, - ?float $passedLoadLimitPerCore, + ?float $passedLoadLimit, ?float $passedSystemLoadAverage, ?int $correctedLimit, ?float $correctedSystemLoadAverage, @@ -76,7 +76,7 @@ public function __construct( ) { $this->passedReservedCpus = $passedReservedCpus; $this->passedLimit = $passedLimit; - $this->passedLoadLimitPerCore = $passedLoadLimitPerCore; + $this->passedLoadLimit = $passedLoadLimit; $this->passedSystemLoadAverage = $passedSystemLoadAverage; $this->correctedLimit = $correctedLimit; $this->correctedSystemLoadAverage = $correctedSystemLoadAverage; From 8a20cafda18bfffd8e95084a5b31b1dcb286db3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Tue, 6 Aug 2024 01:10:32 +0200 Subject: [PATCH 5/6] feat: Allow to pass a negative limit --- src/CpuCoreCounter.php | 87 +++++++++++++++++++++-------------- src/ParallelisationResult.php | 19 ++++---- tests/CpuCoreCounterTest.php | 75 +++++++++++++++++++++++++++--- 3 files changed, 131 insertions(+), 50 deletions(-) diff --git a/src/CpuCoreCounter.php b/src/CpuCoreCounter.php index a41532c..d8a589c 100644 --- a/src/CpuCoreCounter.php +++ b/src/CpuCoreCounter.php @@ -43,42 +43,44 @@ public function __construct(?array $finders = null) } /** - * @param positive-int|0 $reservedCpus Number of CPUs to reserve. This is useful when you want - * to reserve some CPUs for other processes. If the main - * process is going to be busy still, you may want to set - * this value to 1. - * @param positive-int $limit - * @param float|null $loadLimit Element of [0., 1.]. Percentage representing the - * amount of cores that should be used among the available - * resources. For instance, if set to 0.7, it will use 70% - * of the available cores, i.e. if 1 core is reserved, 11 - * cores are available and 5 are busy, it will use 70% - * of (11-1-5)=5 cores, so 3 cores. Set this parameter to null - * to skip this check. Beware that 1 does not mean "no limit", - * but 100% of the _available_ resources, i.e. with the - * previous example, it will return 5 cores. How busy is - * the system is determined by the system load average - * (see $systemLoadAverage). - * @param float|null $systemLoadAverage The system load average. If not provided, it will be - * retrieved using `sys_getloadavg()` to check the load - * of the system in the past minute. Should be a positive - * float. + * @param positive-int|0 $reservedCpus Number of CPUs to reserve. This is useful when you want + * to reserve some CPUs for other processes. If the main + * process is going to be busy still, you may want to set + * this value to 1. + * @param non-zero-int|null $countLimit The maximum number of CPUs to return. If not provided, it + * may look for a limit in the environment variables, e.g. + * KUBERNETES_CPU_LIMIT. If negative, the limit will be + * the total number of cores found minus the absolute value. + * For instance if the system has 10 cores and countLimit=-2, + * then the effective limit considered will be 8. + * @param float|null $loadLimit Element of [0., 1.]. Percentage representing the + * amount of cores that should be used among the available + * resources. For instance, if set to 0.7, it will use 70% + * of the available cores, i.e. if 1 core is reserved, 11 + * cores are available and 5 are busy, it will use 70% + * of (11-1-5)=5 cores, so 3 cores. Set this parameter to null + * to skip this check. Beware that 1 does not mean "no limit", + * but 100% of the _available_ resources, i.e. with the + * previous example, it will return 5 cores. How busy is + * the system is determined by the system load average + * (see $systemLoadAverage). + * @param float|null $systemLoadAverage The system load average. If not provided, it will be + * retrieved using `sys_getloadavg()` to check the load + * of the system in the past minute. Should be a positive + * float. * * @see https://php.net/manual/en/function.sys-getloadavg.php */ public function getAvailableForParallelisation( int $reservedCpus = 0, - ?int $limit = null, + ?int $countLimit = null, ?float $loadLimit = null, ?float $systemLoadAverage = null ): ParallelisationResult { + self::checkCountLimit($countLimit); self::checkLoadLimit($loadLimit); self::checkSystemLoadAverage($systemLoadAverage); - $correctedLimit = null === $limit - ? self::getKubernetesLimit() - : $limit; - $totalCoreCount = $this->getCountWithFallback(1); $availableCores = max(1, $totalCoreCount - $reservedCpus); @@ -94,16 +96,24 @@ public function getAvailableForParallelisation( ); } - if (null !== $correctedLimit && $availableCores > $correctedLimit) { - $availableCores = $correctedLimit; + if (null === $countLimit) { + $correctedCountLimit = self::getKubernetesLimit(); + } else { + $correctedCountLimit = $countLimit > 0 + ? $countLimit + : $totalCoreCount + $countLimit; + } + + if (null !== $correctedCountLimit && $availableCores > $correctedCountLimit) { + $availableCores = $correctedCountLimit; } return new ParallelisationResult( $reservedCpus, - $limit, + $countLimit, $loadLimit, $systemLoadAverage, - $correctedLimit, + $correctedCountLimit, $correctedSystemLoadAverage ?? $systemLoadAverage, $totalCoreCount, (int) $availableCores @@ -210,17 +220,26 @@ public static function getKubernetesLimit(): ?int return $finder->find(); } - private static function checkLoadLimit(?float $loadLimitPerCore): void + private static function checkCountLimit(?int $countLimit): void + { + if (0 === $countLimit) { + throw new InvalidArgumentException( + 'The count limit must be a non zero integer. Got 0.' + ); + } + } + + private static function checkLoadLimit(?float $loadLimit): void { - if (null === $loadLimitPerCore) { + if (null === $loadLimit) { return; } - if ($loadLimitPerCore < 0. || $loadLimitPerCore > 1.) { + if ($loadLimit < 0. || $loadLimit > 1.) { throw new InvalidArgumentException( sprintf( - 'The load limit per core must be in the range [0., 1.], got "%s".', - $loadLimitPerCore + 'The load limit must be in the range [0., 1.], got "%s".', + $loadLimit ) ); } diff --git a/src/ParallelisationResult.php b/src/ParallelisationResult.php index 36e12c5..1f29434 100644 --- a/src/ParallelisationResult.php +++ b/src/ParallelisationResult.php @@ -24,9 +24,9 @@ final class ParallelisationResult public $passedReservedCpus; /** - * @var positive-int|null + * @var non-zero-int|null */ - public $passedLimit; + public $passedCountLimit; /** * @var float|null @@ -39,9 +39,9 @@ final class ParallelisationResult public $passedSystemLoadAverage; /** - * @var positive-int|null + * @var non-zero-int|null */ - public $correctedLimit; + public $correctedCountLimit; /** * @var float|null @@ -60,25 +60,26 @@ final class ParallelisationResult /** * @param positive-int|0 $passedReservedCpus - * @param positive-int|null $passedLimit + * @param non-zero-int|null $passedCountLimit + * @param non-zero-int|null $correctedCountLimit * @param positive-int $totalCoresCount * @param positive-int $availableCpus */ public function __construct( int $passedReservedCpus, - ?int $passedLimit, + ?int $passedCountLimit, ?float $passedLoadLimit, ?float $passedSystemLoadAverage, - ?int $correctedLimit, + ?int $correctedCountLimit, ?float $correctedSystemLoadAverage, int $totalCoresCount, int $availableCpus ) { $this->passedReservedCpus = $passedReservedCpus; - $this->passedLimit = $passedLimit; + $this->passedCountLimit = $passedCountLimit; $this->passedLoadLimit = $passedLoadLimit; $this->passedSystemLoadAverage = $passedSystemLoadAverage; - $this->correctedLimit = $correctedLimit; + $this->correctedCountLimit = $correctedCountLimit; $this->correctedSystemLoadAverage = $correctedSystemLoadAverage; $this->totalCoresCount = $totalCoresCount; $this->availableCpus = $availableCpus; diff --git a/tests/CpuCoreCounterTest.php b/tests/CpuCoreCounterTest.php index 8211dc8..4aa8b67 100644 --- a/tests/CpuCoreCounterTest.php +++ b/tests/CpuCoreCounterTest.php @@ -279,6 +279,16 @@ public static function availableCpuCoreProvider(): iterable 3 ); + yield 'CPU count found, negative limit passed' => AvailableCpuCoresScenario::create( + 5, + [], + 1, + -2, + null, + null, + 3 + ); + yield 'CPU count found, multiple CPUs reserved' => AvailableCpuCoresScenario::create( 5, [], @@ -361,10 +371,61 @@ public static function availableCpuCoreProvider(): iterable } /** - * @dataProvider loadLimitPerCoreProvider + * @dataProvider countLimitProvider + */ + public function test_it_does_not_accept_invalid_count_limit( + int $countLimit, + ?string $expectedExceptionMessage + ): void { + $cpuCoreCounter = new CpuCoreCounter(); + + if (null !== $expectedExceptionMessage) { + $this->expectExceptionMessage($expectedExceptionMessage); + } + + $cpuCoreCounter->getAvailableForParallelisation( + 1, + $countLimit + ); + + if (null === $expectedExceptionMessage) { + $this->addToAssertionCount(1); + } + } + + public static function countLimitProvider(): iterable + { + yield 'below limit' => [ + -2, + null, + ]; + + yield 'within the limit (lower)' => [ + -1, + null, + ]; + + yield 'invalid limit' => [ + 0, + 'The count limit must be a non zero integer. Got 0.', + ]; + + yield 'within the limit (upper)' => [ + 1, + null, + ]; + + yield 'above limit' => [ + 2, + null, + ]; + } + + /** + * @dataProvider loadLimitProvider */ - public function test_it_does_not_accept_invalid_load_limit_per_core( - float $loadLimitPerCore, + public function test_it_does_not_accept_invalid_load_limit( + float $loadLimit, ?string $expectedExceptionMessage ): void { $cpuCoreCounter = new CpuCoreCounter(); @@ -376,7 +437,7 @@ public function test_it_does_not_accept_invalid_load_limit_per_core( $cpuCoreCounter->getAvailableForParallelisation( 1, null, - $loadLimitPerCore + $loadLimit ); if (null === $expectedExceptionMessage) { @@ -384,11 +445,11 @@ public function test_it_does_not_accept_invalid_load_limit_per_core( } } - public static function loadLimitPerCoreProvider(): iterable + public static function loadLimitProvider(): iterable { yield 'below limit' => [ -0.001, - 'The load limit per core must be in the range [0., 1.], got "-0.001".', + 'The load limit must be in the range [0., 1.], got "-0.001".', ]; yield 'within the limit (min)' => [ @@ -403,7 +464,7 @@ public static function loadLimitPerCoreProvider(): iterable yield 'above limit' => [ 1.001, - 'The load limit per core must be in the range [0., 1.], got "1.001".', + 'The load limit must be in the range [0., 1.], got "1.001".', ]; } From 9e15a46ab8b6697c307c14278f80fd147b6d3ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Tue, 6 Aug 2024 01:45:40 +0200 Subject: [PATCH 6/6] fix --- phpstan.src.neon | 3 --- src/CpuCoreCounter.php | 8 ++++++-- tests/AvailableCpuCoresScenario.php | 22 +++++++++++----------- tests/CpuCoreCounterTest.php | 28 ++++++++++++++++++++++++---- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/phpstan.src.neon b/phpstan.src.neon index 78ff7db..a35599e 100644 --- a/phpstan.src.neon +++ b/phpstan.src.neon @@ -15,6 +15,3 @@ parameters: - path: src/CpuCoreCounter.php message: '#ParallelisationResult constructor expects int\<1\, max\>, int given\.#' - - - path: src/ParallelisationResult.php - message: '#\$correctedLimit \(int\<1\, max\>\|null\) does not accept int\|null#' diff --git a/src/CpuCoreCounter.php b/src/CpuCoreCounter.php index d8a589c..a6cd7e4 100644 --- a/src/CpuCoreCounter.php +++ b/src/CpuCoreCounter.php @@ -18,6 +18,7 @@ use Fidry\CpuCoreCounter\Finder\FinderRegistry; use InvalidArgumentException; use function implode; +use function max; use function sprintf; use function sys_getloadavg; use const PHP_EOL; @@ -101,7 +102,7 @@ public function getAvailableForParallelisation( } else { $correctedCountLimit = $countLimit > 0 ? $countLimit - : $totalCoreCount + $countLimit; + : max(1, $totalCoreCount + $countLimit); } if (null !== $correctedCountLimit && $availableCores > $correctedCountLimit) { @@ -213,6 +214,9 @@ public function getFinderAndCores(): array throw NumberOfCpuCoreNotFound::create(); } + /** + * @return positive-int|null + */ public static function getKubernetesLimit(): ?int { $finder = new EnvVariableFinder('KUBERNETES_CPU_LIMIT'); @@ -224,7 +228,7 @@ private static function checkCountLimit(?int $countLimit): void { if (0 === $countLimit) { throw new InvalidArgumentException( - 'The count limit must be a non zero integer. Got 0.' + 'The count limit must be a non zero integer. Got "0".' ); } } diff --git a/tests/AvailableCpuCoresScenario.php b/tests/AvailableCpuCoresScenario.php index ba2309c..0aa8fc9 100644 --- a/tests/AvailableCpuCoresScenario.php +++ b/tests/AvailableCpuCoresScenario.php @@ -28,10 +28,10 @@ final class AvailableCpuCoresScenario public $environmentVariables; /** @var positive-int|0 */ public $reservedCpus; - /** @var positive-int */ - public $limit; + /** @var non-zero-int|null */ + public $countLimit; /** @var float|null */ - public $loadLimitPerCore; + public $loadLimit; /** @var float|null */ public $systemLoadAverage; /** @var positive-int */ @@ -41,14 +41,14 @@ final class AvailableCpuCoresScenario * @param list $finders * @param array $environmentVariables * @param positive-int|0 $reservedCpus - * @param positive-int $limit + * @param non-zero-int|null $countLimit * @param positive-int $expected */ public function __construct( array $finders, array $environmentVariables, int $reservedCpus, - ?int $limit, + ?int $countLimit, ?float $loadLimit, ?float $systemLoadAverage, int $expected @@ -56,8 +56,8 @@ public function __construct( $this->finders = $finders; $this->environmentVariables = $environmentVariables; $this->reservedCpus = $reservedCpus; - $this->limit = $limit; - $this->loadLimitPerCore = $loadLimit; + $this->countLimit = $countLimit; + $this->loadLimit = $loadLimit; $this->systemLoadAverage = $systemLoadAverage; $this->expected = $expected; } @@ -65,8 +65,8 @@ public function __construct( /** * @param positive-int|null $coresCountFound * @param array $environmentVariables - * @param positive-int|0 $reservedCpus - * @param positive-int $limit + * @param positive-int|0|null $reservedCpus + * @param non-zero-int|null $countLimit * @param positive-int $expected * * @return array{self} @@ -75,7 +75,7 @@ public static function create( ?int $coresCountFound, array $environmentVariables, ?int $reservedCpus, - ?int $limit, + ?int $countLimit, ?float $loadLimit, ?float $systemLoadAverage, int $expected @@ -89,7 +89,7 @@ public static function create( $finders, $environmentVariables, $reservedCpus ?? 0, - $limit, + $countLimit, $loadLimit, $systemLoadAverage ?? 0., $expected diff --git a/tests/CpuCoreCounterTest.php b/tests/CpuCoreCounterTest.php index 4aa8b67..fc3873d 100644 --- a/tests/CpuCoreCounterTest.php +++ b/tests/CpuCoreCounterTest.php @@ -179,8 +179,8 @@ public function test_it_can_get_the_number_of_available_cpu_cores_for_parallelis $actual = $counter->getAvailableForParallelisation( $scenario->reservedCpus, - $scenario->limit, - $scenario->loadLimitPerCore, + $scenario->countLimit, + $scenario->loadLimit, $scenario->systemLoadAverage ); @@ -269,7 +269,7 @@ public static function availableCpuCoreProvider(): iterable 5 ); - yield 'CPU count found higher than the limit passed' => AvailableCpuCoresScenario::create( + yield 'CPU count found higher than the count limit passed' => AvailableCpuCoresScenario::create( 5, [], 1, @@ -280,6 +280,26 @@ public static function availableCpuCoreProvider(): iterable ); yield 'CPU count found, negative limit passed' => AvailableCpuCoresScenario::create( + 5, + [], + 0, + -2, + null, + null, + 3 + ); + + yield 'CPU count found, negative limit beyond available resources' => AvailableCpuCoresScenario::create( + 5, + [], + 0, + -10, + null, + null, + 1 + ); + + yield 'CPU count found, with reserved CPU, negative limit passed' => AvailableCpuCoresScenario::create( 5, [], 1, @@ -407,7 +427,7 @@ public static function countLimitProvider(): iterable yield 'invalid limit' => [ 0, - 'The count limit must be a non zero integer. Got 0.', + 'The count limit must be a non zero integer. Got "0".', ]; yield 'within the limit (upper)' => [