[PHP-DEV] [RFC] Add num_available_processors

Hi everyone,

I'm happy to share my first RFC :slight_smile: It proposes adding a small function to retrieve the number of available processors; a feature that's commonly found in other programming languages and one that I believe would be a useful addition to PHP.

The related PR has already received a bit of early traction, and now that the RFC is complete, I'm looking forward to your feedback!

RFC: PHP: rfc:num_available_processors
Patch: feat: add function num_cpus (formerly nproc) by kesselb · Pull Request #11137 · php/php-src · GitHub

Best
Daniel

Hi

In my opinion, the return type should not be nullable.
Returning NULL when the platform (or PHP on that platform) doesn't support getting this information is an anti-pattern.
Instead, availability of the necessary functionality should be checked at configure time and the function should be made conditionally available.
That way, the return type can just be "int".

Kind regards
Niels

On Sat, May 24, 2025, at 19:37, Daniel Kesselberg wrote:

Hi everyone,

I’m happy to share my first RFC :slight_smile: It proposes adding a small function
to retrieve the number of available processors; a feature that’s
commonly found in other programming languages and one that I believe
would be a useful addition to PHP.

The related PR has already received a bit of early traction, and now
that the RFC is complete, I’m looking forward to your feedback!

RFC: https://wiki.php.net/RFC/num_available_processors
Patch: https://github.com/php/php-src/pull/11137

Best
Daniel

Looks good!

My main question is: what is this actually counting? In the RFC it mentions “available processing units” … which means, what? What counts as a “processing unit”? Are we talking about CPU Threads/cores; NPU cores; TPM cores; clocks? GPS? GPU? … a modern computer has many “processing units” for different purposes and workloads. I’m assuming this is CPU Threads, not physical cores? I will refer to CPU Threads as “Logical Cores” so we all don’t get confused since most of us here are programmers and saying “thread” has a different meaning.

Secondly, how is it counting “available”? If I assign PHP to a specific CPU affinity mask (say one logical core), will it return 1, or the total number of logical cores available on my machine? I would expect it to be 1, since PHP only has access to 1, but I can also see the logic in returning the total number.

— Rob

On Sat, May 24, 2025, at 19:42, Niels Dossche wrote:

Hi

In my opinion, the return type should not be nullable.
Returning NULL when the platform (or PHP on that platform) doesn’t support getting this information is an anti-pattern.
Instead, availability of the necessary functionality should be checked at configure time and the function should be made conditionally available.
That way, the return type can just be “int”.

Kind regards
Niels

I’m curious why you say this is an “anti pattern”? I do agree that it should return a number or throw. There are various error conditions it should throw (at least on Linux) so having it throw an exception when there isn’t a way to count any processors makes more sense than returning null.

I also note the patch is using the sysconf method and not checking the error on a negative one result and not properly handling zero. Naturally, this means something might be catastrophically wrong, but it should still throw instead of returning null. It could also be someone patching libc to get around per-core licensing too. I’ve seen the latter more often than the former.

— Rob

On 24/05/2025 21:24, Rob Landers wrote:

On Sat, May 24, 2025, at 19:42, Niels Dossche wrote:

Hi

In my opinion, the return type should not be nullable.
Returning NULL when the platform (or PHP on that platform) doesn't support getting this information is an anti-pattern.
Instead, availability of the necessary functionality should be checked at configure time and the function should be made conditionally available.
That way, the return type can just be "int".

Kind regards
Niels

I’m curious why you say this is an “anti pattern”? I do agree that it should return a number or throw.

If you make the function unconditionally available, yet specifying the return type as ?int, then you give the false impression that it _can_ work even if your platform doesn't support it.

Also: IMO new APIs should fail hard with an exception if they can't do their main task, that's our error "channel".

There are various error conditions it should throw (at least on Linux) so having it throw an exception when there isn’t a way to count any processors makes more sense than returning null.

Throwing is indeed preferable.
What are the error conditions on Linux?

It could also be someone patching libc to get around per-core licensing too. I’ve seen the latter more often than the former.

I don't follow.
In any case, if you run a monkey patched libc and you're breaking the consumer expectations of said libc, then you deserve getting it blown up in your face imo.

Kind regards
Niels

On Sat, May 24, 2025, at 22:28, Niels Dossche wrote:

On 24/05/2025 21:24, Rob Landers wrote:

On Sat, May 24, 2025, at 19:42, Niels Dossche wrote:

Hi

In my opinion, the return type should not be nullable.
Returning NULL when the platform (or PHP on that platform) doesn’t support getting this information is an anti-pattern.
Instead, availability of the necessary functionality should be checked at configure time and the function should be made conditionally available.
That way, the return type can just be “int”.

Kind regards
Niels

I’m curious why you say this is an “anti pattern”? I do agree that it should return a number or throw.

If you make the function unconditionally available, yet specifying the return type as ?int, then you give the false impression that it can work even if your platform doesn’t support it.

Also: IMO new APIs should fail hard with an exception if they can’t do their main task, that’s our error “channel”.

There are various error conditions it should throw (at least on Linux) so having it throw an exception when there isn’t a way to count any processors makes more sense than returning null.

Throwing is indeed preferable.
What are the error conditions on Linux?

It could also be someone patching libc to get around per-core licensing too. I’ve seen the latter more often than the former.
I don’t follow.
In any case, if you run a monkey patched libc and you’re breaking the consumer expectations of said libc, then you deserve getting it blown up in your face imo.

Kind regards
Niels

I think we both agree it should blow up.

Error conditions (for Linux) using the current method in the patch are either -1 or 0. -1 is just a possible return for that function which could indicate that the kernel doesn’t have support for that sysconf value or there are infinite cores — you’d have to check errno.

It might also report 0, which technically isn’t an error (hotplugging cpus for instance). It could also just be a bug. This is a fun one: https://github.com/lxc/lxcfs/issues/469

Also, I wouldn’t be surprised to see a container with very small amounts of cpu allocated getting rounded to zero. But in any case, zero should probably be treated as an error IMHO.

— Rob

On 24 May 2025, at 20:48, Rob Landers <rob@bottled.codes> wrote:

On Sat, May 24, 2025, at 19:37, Daniel Kesselberg wrote:

Hi everyone,

I'm happy to share my first RFC :slight_smile: It proposes adding a small function
to retrieve the number of available processors; a feature that's
commonly found in other programming languages and one that I believe
would be a useful addition to PHP.

The related PR has already received a bit of early traction, and now
that the RFC is complete, I'm looking forward to your feedback!

RFC: PHP: rfc:num_available_processors
Patch: feat: add function num_cpus (formerly nproc) by kesselb · Pull Request #11137 · php/php-src · GitHub

Best
Daniel

Looks good!

My main question is: what is this actually counting? In the RFC it mentions "available processing units" ... which means, what? What counts as a "processing unit"? Are we talking about CPU Threads/cores; NPU cores; TPM cores; clocks? GPS? GPU? ... a modern computer has many "processing units" for different purposes and workloads. I’m assuming this is CPU Threads, not physical cores? I will refer to CPU Threads as "Logical Cores" so we all don’t get confused since most of us here are programmers and saying "thread" has a different meaning.

Secondly, how is it counting "available"? If I assign PHP to a specific CPU affinity mask (say one logical core), will it return 1, or the total number of logical cores available on my machine? I would expect it to be 1, since PHP only has access to 1, but I can also see the logic in returning the total number.

— Rob

Hi Daniel,

I agree with Rob that "processor" is a bit too ambiguous. I'd use the phrase "cpu_core" instead. Yes, technically that's not entirely accurate when hyper-threading is used, but in most cases it's not trivial to distinguish physical cores from logical cores anyway, and "cpu_cores" provides the most understandable abstraction for the vast majority of use cases: deciding how many parallel processes one should use for optimal use of the CPU.

Also, is it really necessary to add "available" as a disambiguator? In other words: are there future plans to add a function that provides the "total" or "unavailable" number of processors? If not, I'd just drop the "available" part.

Finally, from a quick search in php-src there doesn't seem to be any existing function name that starts with `num_`. For the sake of consistency with the existing PHP functions, and similar functionality in other languages, I suggest suffixing the function name with `_count` instead.

So, to wrap this all up, I'd like to respectfully propose the following function name instead:

    cpu_core_count()

Alwin

On Sun, May 25, 2025, at 12:07, Alwin Garside wrote:

On 24 May 2025, at 20:48, Rob Landers <rob@bottled.codes> wrote:

On Sat, May 24, 2025, at 19:37, Daniel Kesselberg wrote:

Hi everyone,

I’m happy to share my first RFC :slight_smile: It proposes adding a small function
to retrieve the number of available processors; a feature that’s
commonly found in other programming languages and one that I believe
would be a useful addition to PHP.

The related PR has already received a bit of early traction, and now
that the RFC is complete, I’m looking forward to your feedback!

RFC: https://wiki.php.net/RFC/num_available_processors
Patch: https://github.com/php/php-src/pull/11137

Best
Daniel

Looks good!

My main question is: what is this actually counting? In the RFC it mentions “available processing units” … which means, what? What counts as a “processing unit”? Are we talking about CPU Threads/cores; NPU cores; TPM cores; clocks? GPS? GPU? … a modern computer has many “processing units” for different purposes and workloads. I’m assuming this is CPU Threads, not physical cores? I will refer to CPU Threads as “Logical Cores” so we all don’t get confused since most of us here are programmers and saying “thread” has a different meaning.

Secondly, how is it counting “available”? If I assign PHP to a specific CPU affinity mask (say one logical core), will it return 1, or the total number of logical cores available on my machine? I would expect it to be 1, since PHP only has access to 1, but I can also see the logic in returning the total number.

— Rob

Hi Daniel,

I agree with Rob that “processor” is a bit too ambiguous. I’d use the phrase “cpu_core” instead. Yes, technically that’s not entirely accurate when hyper-threading is used, but in most cases it’s not trivial to distinguish physical cores from logical cores anyway, and “cpu_cores” provides the most understandable abstraction for the vast majority of use cases: deciding how many parallel processes one should use for optimal use of the CPU.

Yes, that is why I was curious as to the method it was using to return the number. Using the sysconf method will just return the total number of available cores. However, in Linux there is also the sched_getaffinity method which returns the total number of cores allocated to the current process (which may be lower). For example, when sharing a webserver and a worker queue on a single system, I may only allocate 1/3 of my cores (via taskset) to the worker queue, and 1/3 cores to the webserver and the last 1/3 for other tasks (such as a database). This prevents the worker queue from taking over the entire system and slowing down my web requests and database.

If we have 12 cores and I’ve only allocated 4 cores for PHP, I would want this function to tell my worker queue that it only has 4 cores, using the system above — not 12. This probably doesn’t matter too much when this is the only process on the machine. However, in any serious production environment, you want to be able to prevent a run-away process/job from harming other unrelated aspects of the system.

You can also see this behaviour with nproc:

❯ taskset -c 0 nproc
1

❯ nproc
16

— Rob

Hi

Am 2025-05-25 12:07, schrieb Alwin Garside:

Finally, from a quick search in php-src there doesn't seem to be any existing function name that starts with `num_`. For the sake of consistency with the existing PHP functions, and similar functionality in other languages, I suggest suffixing the function name with `_count` instead.

So, to wrap this all up, I'd like to respectfully propose the following function name instead:

    cpu_core_count()

Naming is a good point indeed and one that I also hinted at with my comment on the PR: feat: add function num_cpus (formerly nproc) by kesselb · Pull Request #11137 · php/php-src · GitHub

I very strongly disagree with adding more “randomly named” functions to ext/standard, the times of doing that are over. I would also claim that it violates the “Coding Standards and Naming Policy” (policies/coding-standards-and-naming.rst at main · php/policies · GitHub) in spirit.

At the very least the function should have a clear “prefix”. `sys` as in `sys_getloadavg()` or `sys_get_temp_dir()` would probably work. Alternatively `get` as in `getmyuid()` or `getrusage()` would also work.

As I've indicated in the PR, my preference would be creating a new namespace and moving over the matching functions together with a discussion of some namespace structure, including matching exceptions (see PHP: rfc:extension_exceptions).

Best regards
Tim DĂĽsterhus

On Sat, May 24, 2025, at 12:37 PM, Daniel Kesselberg wrote:

Hi everyone,

I'm happy to share my first RFC :slight_smile: It proposes adding a small function
to retrieve the number of available processors; a feature that's
commonly found in other programming languages and one that I believe
would be a useful addition to PHP.

The related PR has already received a bit of early traction, and now
that the RFC is complete, I'm looking forward to your feedback!

RFC: PHP: rfc:num_available_processors
Patch: feat: add function num_cpus (formerly nproc) by kesselb · Pull Request #11137 · php/php-src · GitHub

Best
Daniel

I don't really have a problem with adding a function to expose this value, in concept. But what is the use case, exactly? The RFC mentions something about php-cs-fixer in passing in half a sentence, but doesn't explain it further. Please explain further what value this has for core, rather than the parallels extension. (In the RFC, not just here on list.)

--Larry Garfield

Le samedi 24 mai 2025, 19:42:34 heure d’été d’Europe centrale Niels Dossche a écrit :

In my opinion, the return type should not be nullable.
Returning NULL when the platform (or PHP on that platform) doesn't support getting this information is an anti-pattern.
Instead, availability of the necessary functionality should be checked at configure time and the function should be made conditionally available.
That way, the return type can just be "int".

I strongly disagree here, having PHP function not always available is a mess, and forces function_exists checks all over the code. It feels archaic.

I get the idea of throwing instead of returning NULL for errors, but the idea behind the nullable return is to get this nice thing:
$cpus = num_available_cpus() ?? 1;

If the function throws on error, this gets more convoluted. Maybe it can be replaced by a parameter $defaultNumber and only throw when no default is given?

Regarding the CPUs available for PHP or total number discussion, maybe that justifies the "available" keyword in the name and it returns only those available to PHP, and we add a second function for total number?
Or as some people suggested in the PR, we add a namespaced class with methods for this.

CĂ´me

On 5/24/25 15:28, Niels Dossche wrote:

On 24/05/2025 21:24, Rob Landers wrote:

On Sat, May 24, 2025, at 19:42, Niels Dossche wrote:

Hi

In my opinion, the return type should not be nullable.
Returning NULL when the platform (or PHP on that platform) doesn't support getting this information is an anti-pattern.
Instead, availability of the necessary functionality should be checked at configure time and the function should be made conditionally available.
That way, the return type can just be "int".

Kind regards
Niels

I’m curious why you say this is an “anti pattern”? I do agree that it should return a number or throw.

If you make the function unconditionally available, yet specifying the return type as ?int, then you give the false impression that it _can_ work even if your platform doesn't support it.

Also: IMO new APIs should fail hard with an exception if they can't do their main task, that's our error "channel".

There are various error conditions it should throw (at least on Linux) so having it throw an exception when there isn’t a way to count any processors makes more sense than returning null.

Throwing is indeed preferable.
What are the error conditions on Linux?

Are you suggesting that the function itself not be available or that it throws when you attempt to call it on a system that doesn't support it?

I don't like the idea of the function not being available if the system doesn't support the functionality. Users would have to call function_exists() before using the function, in those cases.

Random thought: if the system doesn't support the functionality, would it be wrong to respond with a default value of 1 or 0? Presumably, all systems have at least 1 core (maybe that's a naïve assumption). (I'm not in favor of this approach—it's just a random thought.)

IMO, it's best to throw an exception when calling the function if the system doesn't support it.

Cheers,
Ben

On 27 May 2025 17:37:47 BST, Ben Ramsey <ramsey@php.net> wrote:

Are you suggesting that the function itself not be available or that it throws when you attempt to call it on a system that doesn't support it?

I don't like the idea of the function not being available if the system doesn't support the functionality. Users would have to call function_exists() before using the function, in those cases.

This has been the accepted practise for years, if not decades.

If functionality isn't available, the function shouldn't exist. We do the same for posix (such as posix_getrlimit) and DNS related functions.

This also allows for polyfills.

cheers
Derick

On 5/27/25 12:01, Derick Rethans wrote:

On 27 May 2025 17:37:47 BST, Ben Ramsey <ramsey@php.net> wrote:

Are you suggesting that the function itself not be available or that it throws when you attempt to call it on a system that doesn't support it?

I don't like the idea of the function not being available if the system doesn't support the functionality. Users would have to call function_exists() before using the function, in those cases.

This has been the accepted practise for years, if not decades.

If functionality isn't available, the function shouldn't exist. We do the same for posix (such as posix_getrlimit) and DNS related functions.

This also allows for polyfills.

cheers
Derick

I see. Carry on, then. :slight_smile:

Cheers,
Ben