Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize result representation for BcMathCalculator #748

Merged

Conversation

bcremer
Copy link
Contributor

@bcremer bcremer commented Apr 6, 2023

Optimize the result representation of some BcMathCalculator operations for faster usage in \Money\Money::__construct.

This will provide a 50% performance boost for some operations.

> vendor/bin/phpbench run --retry-threshold=3 --iterations=15 --revs=1000  --warmup=2 'benchmark/MoneyOperationBench.php' '--ref=before_bcmath_scale'
PHPBench (1.2.10) running benchmarks... #standwithukraine
with configuration file: /home/pushapidev/current/moneyphp/phpbench.json
with PHP version 8.1.17, xdebug ✔, opcache ✔
comparing [actual vs. before_bcmatch_scale]

\Benchmark\Money\MoneyOperationBench

    benchAdd................................R4 I9 - [Mo0.298μs vs. Mo0.610μs] -51.12% (±0.63%)
    benchSubtract...........................R1 I5 - [Mo0.298μs vs. Mo0.611μs] -51.34% (±1.04%)
    benchMultiply...........................R1 I12 - [Mo0.622μs vs. Mo0.629μs] -1.16% (±1.30%)
    benchDivide.............................R1 I13 - [Mo0.731μs vs. Mo0.726μs] +0.67% (±1.08%)
    benchSum................................R1 I12 - [Mo0.646μs vs. Mo1.063μs] -39.23% (±0.65%)
    benchMin................................R1 I14 - [Mo0.494μs vs. Mo0.493μs] +0.11% (±1.01%)
    benchMax................................R1 I6 - [Mo0.509μs vs. Mo0.512μs] -0.48% (±0.74%)
    benchAvg................................R1 I3 - [Mo1.462μs vs. Mo1.822μs] -19.79% (±1.40%)
    benchRatioOf............................R1 I0 - [Mo0.365μs vs. Mo0.349μs] +4.68% (±1.99%)
    benchMod................................R6 I13 - [Mo0.382μs vs. Mo0.383μs] -0.48% (±1.06%)
    benchIsSameCurrency.....................R1 I6 - [Mo0.059μs vs. Mo0.060μs] -1.88% (±1.31%)
    benchIsZero.............................R1 I11 - [Mo0.106μs vs. Mo0.105μs] +0.25% (±1.22%)
    benchAbsolute...........................R3 I14 - [Mo0.157μs vs. Mo0.157μs] -0.27% (±1.18%)
    benchNegative...........................R1 I9 - [Mo0.392μs vs. Mo0.710μs] -44.84% (±0.90%)
    benchIsPositive.........................R2 I6 - [Mo0.102μs vs. Mo0.102μs] -0.03% (±0.86%)
    benchCompare............................R3 I14 - [Mo0.133μs vs. Mo0.133μs] +0.06% (±0.98%)
    benchLessThan...........................R2 I3 - [Mo0.149μs vs. Mo0.149μs] -0.07% (±0.75%)
    benchLessThanOrEqual....................R2 I8 - [Mo0.149μs vs. Mo0.149μs] -0.01% (±1.25%)
    benchEquals.............................R1 I4 - [Mo0.169μs vs. Mo0.169μs] +0.13% (±0.93%)
    benchGreaterThan........................R1 I7 - [Mo0.149μs vs. Mo0.149μs] +0.11% (±1.60%)
    benchGreaterThanOrEqual.................R1 I14 - [Mo0.149μs vs. Mo0.149μs] +0.28% (±1.28%)

Subjects: 21, Assertions: 0, Failures: 0, Errors: 0

For full history: #747

@bcremer bcremer force-pushed the improve-BcMathCalculator-performance branch from c1ac815 to d5389be Compare April 6, 2023 11:46
@bcremer bcremer force-pushed the improve-BcMathCalculator-performance branch from d5389be to 0bddd72 Compare April 6, 2023 11:48
@bcremer
Copy link
Contributor Author

bcremer commented Apr 6, 2023

@Ocramius You implemented
Tests\Money\Calculator\LocaleAwareBcMathCalculatorTest::itUsesScaleForSubtract
\Tests\Money\Calculator\BcMathCalculatorTest::itUsesScaleForSubtract

Both are currently failing with my changes.

From my understanding \Money\Calculator\BcMathCalculator::add should only be called with int like strings and never with float like strings.
Also the \Tests\Money\Calculator\CalculatorTestCase only uses positive-int for inputs.

@Ocramius
Copy link
Contributor

Ocramius commented Apr 6, 2023

@bcremer changes seem to indicate that removing the scale broke everything in the test suite?

@bcremer
Copy link
Contributor Author

bcremer commented Apr 6, 2023

It's only the explicit itUsesScale* tests that fail. All other real-world tests are running fine.
The test was introduced by @frederikbosch in https://github.com/moneyphp/money/pull/528/files but the implementation changed quite a bit some time ago.

@frederikbosch
Copy link
Member

@bcremer I cannot recall or see why I would have done that. Maybe it was some preliminary work for PreciseMoney, a project that never made the finish line. If no other fails, I think it is fine to change the scale to zero for add and subtract operations.

Copy link
Contributor

@Ocramius Ocramius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm saying that your patch should NOT touch src/Calculator/BcMathCalculator.php unless there's a strong reason to do it.

The fact that tests cover that is because there's a BC boundary there.

@frederikbosch
Copy link
Member

Hmm, maybe @Ocramius is right. In the end of the day this is a performance patch. It should deal with all features in the library.

@bcremer bcremer force-pushed the improve-BcMathCalculator-performance branch from 4e8ea5e to 2ad0ec7 Compare April 6, 2023 12:44
@bcremer
Copy link
Contributor Author

bcremer commented Apr 6, 2023

I see your point. I pushed a solution that will speedup the "real world path" as well as maintain the public api.

This will still result in a 50% improvement for certain operations:

comparing [actual vs. before_bcmath_scale]

\Benchmark\Money\MoneyOperationBench

    benchAdd................................R5 I14 - [Mo0.324μs vs. Mo0.646μs] -49.89% (±0.52%)
    benchSubtract...........................R1 I4 - [Mo0.323μs vs. Mo0.648μs] -50.24% (±0.60%)
    benchMultiply...........................R1 I12 - [Mo0.670μs vs. Mo0.670μs] -0.00% (±1.14%)
    benchDivide.............................R1 I7 - [Mo0.775μs vs. Mo0.780μs] -0.65% (±0.98%)
    benchSum................................R1 I9 - [Mo0.713μs vs. Mo1.072μs] -33.54% (±0.98%)
    benchMin................................R1 I6 - [Mo0.513μs vs. Mo0.518μs] -0.88% (±1.29%)
    benchMax................................R1 I3 - [Mo0.527μs vs. Mo0.530μs] -0.60% (±1.10%)
    benchAvg................................R1 I0 - [Mo1.596μs vs. Mo1.936μs] -17.59% (±1.10%)
    benchRatioOf............................R1 I4 - [Mo0.359μs vs. Mo0.358μs] +0.45% (±0.47%)
    benchMod................................R4 I13 - [Mo0.396μs vs. Mo0.394μs] +0.50% (±1.15%)
    benchIsSameCurrency.....................R1 I6 - [Mo0.061μs vs. Mo0.061μs] +0.01% (±0.97%)
    benchIsZero.............................R1 I3 - [Mo0.108μs vs. Mo0.109μs] -0.72% (±1.07%)
    benchAbsolute...........................R4 I11 - [Mo0.161μs vs. Mo0.163μs] -1.09% (±0.86%)
    benchNegative...........................R2 I10 - [Mo0.422μs vs. Mo0.755μs] -44.15% (±0.81%)
    benchIsPositive.........................R1 I13 - [Mo0.108μs vs. Mo0.107μs] +0.98% (±1.05%)
    benchCompare............................R1 I13 - [Mo0.134μs vs. Mo0.135μs] -1.05% (±1.27%)
    benchLessThan...........................R5 I11 - [Mo0.154μs vs. Mo0.154μs] +0.02% (±1.07%)
    benchLessThanOrEqual....................R2 I5 - [Mo0.154μs vs. Mo0.154μs] +0.34% (±0.99%)
    benchEquals.............................R1 I8 - [Mo0.174μs vs. Mo0.174μs] -0.04% (±1.02%)
    benchGreaterThan........................R5 I13 - [Mo0.154μs vs. Mo0.154μs] +0.07% (±1.07%)
    benchGreaterThanOrEqual.................R1 I12 - [Mo0.154μs vs. Mo0.155μs] -0.88% (±0.70%)

@bcremer bcremer force-pushed the improve-BcMathCalculator-performance branch from 2ad0ec7 to c44e629 Compare April 6, 2023 12:48
@frederikbosch
Copy link
Member

I think this good to go, @Ocramius approved?

benchmark/MoneyOperationBench.php Show resolved Hide resolved
@@ -31,13 +32,17 @@ public static function compare(string $a, string $b): int
/** @psalm-pure */
public static function add(string $amount, string $addend): string
{
return bcadd($amount, $addend, self::SCALE);
$scale = str_contains($amount, '.') ? self::SCALE : 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is checking $amount, but not $addend

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd really just leave these methods alone :P

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, why not patch bcmath itself to ignore the scale directly, when there's no decimal stuff going on? :P

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could submit a PR for that too, agreed. Let's do both.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be a BC break in the bcmath extension wouldn't it?

It's not about ignoring the scale during the bcmath operation but about the return value of bcmath.

See previous PR for detailed explanation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a breakage, if there's no behavioral change.

If there's noticeable performance overhead, a large chunk of the PHP community could benefit from it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need beauviour change for the performance improvement in the MoneyPHP ctor.

See #747 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, lemme rephrase: I was saying that patching https://github.com/php/php-src/ is not a BC break if there's no behavioral change.

Copy link
Contributor Author

@bcremer bcremer Apr 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we talk past each other.

bcadd('5', '15', 0) and bcadd('5', '15', 14) are already comparable performance wise.

What is different is the returned string value:

bcadd('5', '15', 0); // string(2) "20"
bcadd('5', '15', 14); // string(2) "20.00000000000000"

I'd need to change in the php-core.

- bcadd('5', '15', 14); // string(2) "20.00000000000000"
+ bcadd('5', '15', 14); // string(2) "20"

I consider this a major BC break.

The actual performance improvement comes from passing the calculatores return value into Money::__construct

return new Money("20", $currenty); // fast
return new Money("20.00000000000000", $currency); // slow

This is due to additional number parsing if the passed string is valid for FILTER_VALIDATE_INT :

if (filter_var($amount, FILTER_VALIDATE_INT) === false) {

That given maybe it's would be a better fix to improve that validation for numeric strings containing int values.

So a fast coercion from 20.00000000000000 to 20.

I hope that clears things up. Sorry for splitting my PR into two, that whas not helpful to preserve context.

@bcremer bcremer force-pushed the improve-BcMathCalculator-performance branch from c44e629 to e3d208a Compare April 6, 2023 13:00
@frederikbosch frederikbosch merged commit 8162ab8 into moneyphp:master Apr 11, 2023
@frederikbosch
Copy link
Member

Thanks @bcremer for your effort and PR for the optimization. @Ocramius thanks for your feedback!

frederikbosch pushed a commit that referenced this pull request Nov 22, 2023
* Optimize result representation for BcMathCalculator::add and BcMathCalculator::substract
Copy link

@Tunechi2323 Tunechi2323 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything is good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants