Hi internals,
I've been working on improving performance of BCMath lately, and I found that I can get the div and mod in one calculation. This is obviously faster than calculating it twice separately.
Do you think there's a demand for this feature?
e.g.
[$quot, $rem] = bcdivmod('123', '2');
// $quot is '61', $rem is '1'
The naming and return value scheme is inspired by Python and Ruby.
Of course, if this is added, equivalent functionality will be added to the Number class.
Regards,
Saki
Hi internals,
I've been working on improving performance of BCMath lately, and I found that I can get the div and mod in one calculation. This is obviously faster than calculating it twice separately.
Do you think there's a demand for this feature?
Since, when calculating either the div or the mod you basically get the
other for free (as you've found), and they often go together in use (I
have some number of things to distributed among some number of columns:
I end up with this many things in each column and this many left
over) so I can definitely see a convenience in this.
Otherwise, if you want both, you essentially end up doing the same
computation twice:
$rem = bcmod($number, $modulus);
$quot = bcdiv(bcsub($number, $rem), $modulus);
Hi internals,
I've been working on improving performance of BCMath lately, and I
found that I can get the div and mod in one calculation. This is
obviously faster than calculating it twice separately.Do you think there's a demand for this feature?
e.g.
[$quot, $rem] = bcdivmod('123', '2'); // $quot is '61', $rem is '1'
The naming and return value scheme is inspired by Python and Ruby.
Of course, if this is added, equivalent functionality will be added to
the Number class.Regards,
Saki
This isn't something I'm likely to ever use as I don't do complex math much, but it seems like a reasonable optimization for those that do. No objection.
My only question is the pseudo-tuple return, which is rarely used in PHP (although I've used it myself, I think exactly once), and I think this would be the first use of it in a built in library. (I may be wrong on that.) I don't have a particular alternative to suggest, just flagging that as the main part that could warrant discussion.
--Larry Garfield
Hi,
I've been working on improving performance of BCMath lately, and I found
that I can get the div and mod in one calculation. This is obviously faster
than calculating it twice separately.Do you think there's a demand for this feature?
e.g.
[$quot, $rem] = bcdivmod('123', '2'); // $quot is '61', $rem is '1'
The naming and return value scheme is inspired by Python and Ruby.
Of course, if this is added, equivalent functionality will be added to the
Number class.Regards,
Saki
I would definitely use this in brick/math:
https://github.com/brick/math/blob/0.12.1/src/Internal/Calculator/BcMathCalculator.php#L43-L49
This isn't something I'm likely to ever use as I don't do complex math
much, but it seems like a reasonable optimization for those that do. No
objection.My only question is the pseudo-tuple return, which is rarely used in PHP
(although I've used it myself, I think exactly once), and I think this
would be the first use of it in a built in library. (I may be wrong on
that.) I don't have a particular alternative to suggest, just flagging
that as the main part that could warrant discussion.--Larry Garfield
It's actually already used by gmp_div_qr(), so +1 from me.
- Benjamin
---- On Tue, 25 Jun 2024 10:11:10 +0100 Saki Takamachi wrote ---
Hi internals,
I've been working on improving performance of BCMath lately, and I found that I can get the div and mod in one calculation. This is obviously faster than calculating it twice separately.
Do you think there's a demand for this feature?
e.g.
[$quot, $rem] = bcdivmod('123', '2'); // $quot is '61', $rem is '1'
The naming and return value scheme is inspired by Python and Ruby.
Of course, if this is added, equivalent functionality will be added to the Number class.
I'm wondering whether this needs to be a change to the API, or if it might be better implemented as a purely internal optimisation to BCMath / Number - in some way memoize both the quotient and the remainder when either div or mod is called, and then document for users that calling one after the other will be extremely cheap to run.
But maybe that brings up too many questions about memory usage - e.g. is the memory for this reserved in advance for each instance of Number, or allocated when one of these functions is called, and should it be freed even while the object remains in scope to avoid using lots of memory if many numbers are divided or modded.
Thanks all,
My only question is the pseudo-tuple return, which is rarely used in PHP (although I've used it myself, I think exactly once), and I think this would be the first use of it in a built in library. (I may be wrong on that.) I don't have a particular alternative to suggest, just flagging that as the main part that could warrant discussion.
It's actually already used by gmp_div_qr(), so +1 from me.
I predicted this would probably be on the agenda. Another idea is to pass arguments by reference, like in exec()
.
Personally, I find something like a tuple easier to use. However, without generics, all we know is that the return type is an array, which may be inconvenient in terms of IDE completion, etc.
I'm wondering whether this needs to be a change to the API, or if it might be better implemented as a purely internal optimisation to BCMath / Number - in some way memoize both the quotient and the remainder when either div or mod is called, and then document for users that calling one after the other will be extremely cheap to run.
But maybe that brings up too many questions about memory usage - e.g. is the memory for this reserved in advance for each instance of Number, or allocated when one of these functions is called, and should it be freed even while the object remains in scope to avoid using lots of memory if many numbers are divided or modded.
I am considering implementing this not only as a Number class, but also as a function.
Also, it is not practical to record the remainder in the Number object resulting from the division. This is because there can be objects whose division results are equal but whose remainders are different.
In the discussion of the Number class, it was argued that objects should not have too much state.
Increasing the state of surplus is not desirable in this respect, as it contradicts the previous argument.
Regards,
Saki
---- On Tue, 25 Jun 2024 17:29:58 +0100 Saki Takamachi wrote ---
Also, it is not practical to record the remainder in the Number object resulting from the division. This is because there can be objects whose division results are equal but whose remainders are different.
In the discussion of the Number class, it was argued that objects should not have too much state.
Increasing the state of surplus is not desirable in this respect, as it contradicts the previous argument.
If anything I was thinking of recording the remainder in the the original number object, not the one returned (or possibly in something like a WeakMap keyed to it). So it does increase state and makes it technically no longer immutable, but that change in state would not be detectable from outside except by profiling performance. It's state that would have to be ignored in the equality check.
Hi Barney,
If anything I was thinking of recording the remainder in the the original number object, not the one returned (or possibly in something like a WeakMap keyed to it). So it does increase state and makes it technically no longer immutable, but that change in state would not be detectable from outside except by profiling performance. It's state that would have to be ignored in the equality check.
I see, I understand.
But I don't think that's wise. For example, if we reversed the order of the div and mod, there would be no cache and we wouldn't get the speed boost. (Or does it cache the quotient as well?)
Also, even if the order is as expected, if another division is performed in between, the cache will be overwritten and it will become meaningless.
The main concern is the cost of having to mod data during division for the surplus that we may never use.
There is the issue of data capacity, but the extra processing such as memory allocation will inevitably occur, so for those who don't need mod, the change will make slow down.
Regards,
Saki
I see, I understand.
But I don't think that's wise. For example, if we reversed the order of
the div and mod, there would be no cache and we wouldn't get the speed
boost. (Or does it cache the quotient as well?)
I don't think the cache is a good idea either, for the reasons you
mentioned.
I predicted this would probably be on the agenda. Another idea is to pass
arguments by reference, like in
exec()
.Personally, I find something like a tuple easier to use. However, without
generics, all we know is that the return type is an array, which may be
inconvenient in terms of IDE completion, etc.
As for tuple vs reference, I think the general direction is to move away
from references as much as possible, and AFAIK references actually make
things harder for IDEs and static analysis tools, whereas the tuple syntax
array{string, string} is well understood at least by PhpStorm, Psalm and
PHPStan, which can correctly type the function's return value in their
stubs.
— Benjamin
Hey Benjamin,
On Tue, 25 Jun 2024 at 22:24, Benjamin Morel benjamin.morel@gmail.com
wrote:
I predicted this would probably be on the agenda. Another idea is to pass
arguments by reference, like in
exec()
.Personally, I find something like a tuple easier to use. However, without
generics, all we know is that the return type is an array, which may be
inconvenient in terms of IDE completion, etc.As for tuple vs reference, I think the general direction is to move away
from references as much as possible, and AFAIK references actually make
things harder for IDEs and static analysis tools, whereas the tuple syntax
array{string, string} is well understood at least by PhpStorm, Psalm and
PHPStan, which can correctly type the function's return value in their
stubs.
Full ack on this.
I'm wondering if an array vs an object allocation makes a difference here,
or if the amount of bcdivmod()
execution dwarfs this sort of concern?
Thinking:
$result = \bcdivmod('123', '2');
echo $result->quotient; // '61'
echo $result->remainder; // '1'
No idea if that's relevant, so I'm throwing it in the room.
Marco Pivetta
Hi Marco, Benjamin,
As for tuple vs reference, I think the general direction is to move away from references as much as possible, and AFAIK references actually make things harder for IDEs and static analysis tools, whereas the tuple syntax array{string, string} is well understood at least by PhpStorm, Psalm and PHPStan, which can correctly type the function's return value in their stubs.
I see, I agree. This problem seems to be solved by using PHPDoc.
I'm wondering if an array vs an object allocation makes a difference here, or if the amount of
bcdivmod()
execution dwarfs this sort of concern?Thinking:
$result = \bcdivmod('123', '2'); echo $result->quotient; // '61' echo $result->remainder; // '1'
No idea if that's relevant, so I'm throwing it in the room.
BCMath is significantly faster in master, so its cost may have a significant impact.
For reference, here is a speed comparison with 8.3 on my env. The benchmark used is the code from ext-decimal, which is often introduced in the context of "BCMath is slow”. The unit of all measurement results is "seconds".
https://php-decimal.github.io/#performance
8.3
- add int: 3.7771
- add string: 3.0387
- sub int: 3.491
- sub string: 3.0248
- mul int: 5.3318
- mul string: 5.7315
- div int: 10.6659
- div string: 25.762
Master
- add int: 1.72
- add string: 1.4444
- sub int: 1.763
- sub string: 1.4745
- mul int: 2.2038
- mul string: 2.0621
- div int: 2.8515
- div string: 2.9411
Regards,
Saki