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

Operations with unreduced fractions #65

Merged
merged 25 commits into from
May 20, 2024

Conversation

lipchev
Copy link
Contributor

@lipchev lipchev commented May 12, 2024

Following the discussion in #49 here is a POC with special handling for the operations of the non-reduced fractions.

Motivations:

  1. Given that all existing operations return a normalized fraction- the current code-base doesn't offer much use for the concept of a non-normalized fractions (other than the initial construction speed).
  2. There aren't many ways of reducing a decimal number (unless it has trailing zeros) - with the result having ~ the same size.
  3. Operations with BigInteger that have a trivial value (smaller than int.MaxValue) perform the same way, in which case using the GDC reduction on every operation becomes a lot more expensive.
  4. When fractions are constructed using similarly-formatted numbers (e.g. with 2 decimal places) without normalizing the denominator, subsequent operations such as Add/Subtract are much faster
  5. Unless constrained, the Multiply and Divide operations would eventually grow beyond the trivial size and become slow to work with.

I've tried to address most of these using the following strategy (breaking change):

Assuming:

NF = Non-normalized (improper) fraction
F = normalized fraction
⊙ = mathematical operation having two operants (+, -, *, /, %)
F ⊙ F = F
NF ⊙ F = NF
F ⊙ NF = NF
NF ⊙ NF = NF

I initially tried to address 5) by replicating the rules that are employed by the decimal type (these are the only docs I was able to find on the subject).
However that didn't satisfy the constraints that I had set for myself:

  1. x * 1000 / 1000 == x should be true
  2. x / 1000 * 1000 == x should be true
  3. x * 2 * 5 should be the same as x * 10

Note, that this isn't a strict requirement for 5) but, if possible, I wanted to be able to express the following concept:
1.00000 g * 1000 = 1000.0 mg and returning back to 1.00000 when dividing by 1000 (as if the user is switching the balance display from grams to milligrams).

The reduction strategy for the multiplication is to:

            ReduceTerms(ref thisNumerator, ref otherDenominator); 
            ReduceTerms(ref otherNumerator, ref thisDenominator);
            return new Fraction(true,
                thisNumerator * otherNumerator,
                thisDenominator * otherDenominator);

While the Division is just expanding:

            return new Fraction(true,
                thisNumerator * otherDenominator,
                thisDenominator * otherNumerator);

Despite my best efforts, I wasn't able to balance the operations- everything i tried ended up failing one of 1) or 2) or 3) - if you can spot a possible reduction scheme that works, that would be great.
This is currently biased against the Multiply operator, but is still faster than the reduced version (on most benchmarks).

  • Operations with non-reduced fractions should now produce non-reduced fractions (Add/Multiply/Divide/Remainder) while preserving the number precision (denominator)
  • Added an optional reduceTerms parameter for (most) operations.
  • Add parameter to the FromString overloads
  • Replaced the Decimal format of the FractionDebugView with the ToDecimalWithTrailingZeros() variant
  • Possibly include a special string format that preserves the trailing zeros

Possible issues:

  • Fraction.Pow(value, 2) is not consistent with the value * value (it won't be ease to make it work with the current strategy)
  • Fraction.FromString(..) using the scientific format currently ignores any trailing zero after the decimal point (but before the E).
  • ToDecimalWithTrailingZeros() performance may not be optimal (but I couldn't figure out anything better..)

@danm-de
I think this more or less good for review. I'm still not 100% sure about whether not to switch the strategy from reduce on Multiply to reduce on Divide .
I'll see if I can make another PR later -It would probably resolve most of the above listed issues. and would probably improve the performance (overall).

PS
Feel free to post modifications directly on my branch if you prefer.

…tions (Add/Mutiply/Divide) while preserving the number precision (denominator)

added an optional reduceTerms parameter for (most) operations
- implemented the non-normalized Remainder (%)
- optimized the Multiply/Divide/Remainder operations (both normalized and non-normalized)
…al using the FromString / TryParse methods

updated the comments, tests and benchmarks for the string parsing surface
added an additional benchmark perfoming consecutive math operations using standard decimal fractions
@lipchev lipchev changed the title Operations with unreduced fractions (WIP) Operations with unreduced fractions May 19, 2024
@lipchev
Copy link
Contributor Author

lipchev commented May 19, 2024

Here are the BasicMath benchmarks:

image
image

At first glance this looks a win-some-loose-some type of scenario, however we should take into account:

  1. The two extra GCD - reductions for the construction of the operands.
  2. The fact that the values tested in these benchmarks are mostly extreme values (often non-terminating, and almost never having the same denominators).

@lipchev
Copy link
Contributor Author

lipchev commented May 19, 2024

2. The fact that the values tested in these benchmarks are mostly extreme values  (often non-terminating, and almost never having the same denominators).

Here is the absolute times for two numbers with the same number of decimals:

image

I originally assumed that by not reducing the operands on both operations (Multiply and Divide) there would be significant performance degradation by performing the same type of operation over and over (for the benefit of the Add and Subtract operations) however that did not turn out to be the case (at least not for the many values I've tested).
Here's an overview with 20 operations performed on random fractions with denominators: 1, 10, 100, 1000, and 10000:

image
image

@lipchev
Copy link
Contributor Author

lipchev commented May 19, 2024

Here are just the additions (without the allocations- they are always 32B):

image
image

@lipchev
Copy link
Contributor Author

lipchev commented May 19, 2024

Here are just the multiplications (currently using two GCD-reductions):

image
image

image
image

@lipchev
Copy link
Contributor Author

lipchev commented May 19, 2024

Here are just the divisions (currently using no GCD-reductions):

image
image

image
image

@lipchev
Copy link
Contributor Author

lipchev commented May 19, 2024

Finally there is the ExampleScenarioBenchmark- I tried to create something like the example from the clinic, mostly aiming to verify how the decimal point shifting would look.. There does appear to be some kind of a relationship but in end I'm not sure that if I reversed the reduction strategy (from reduce on multiplication to reduce on division) this wouldn't make more sense..

Anyway here are the results:

Method Job Runtime Mean Error StdDev Ratio Gen0 Gen1 Allocated Alloc Ratio
PrepareSchedulesReduced MediumRun-.NET 8.0 .NET 8.0 9.592 μs 0.0469 μs 0.0687 μs 1.00 0.8698 0.0305 14.38 KB 1.00
PrepareSchedulesNonReduced MediumRun-.NET 8.0 .NET 8.0 8.598 μs 0.0599 μs 0.0878 μs 0.90 0.9003 0.0305 14.81 KB 1.03
                     
PrepareSchedulesReduced MediumRun-.NET Framework 4.8 .NET Framework 4.8 25.779 μs 0.0840 μs 0.1231 μs 1.00 4.7913 0.1221 29.52 KB 1.00
PrepareSchedulesNonReduced MediumRun-.NET Framework 4.8 .NET Framework 4.8 24.344 μs 0.0987 μs 0.1478 μs 0.94 4.8523 0.1221 29.96 KB 1.01

@danm-de
Copy link
Owner

danm-de commented May 19, 2024

Despite my best efforts, I wasn't able to balance the operations- everything i tried ended up failing one of 1) or 2) or 3) - if you can spot a possible reduction scheme that works, that would be great.

I'm sorry. Unfortunately, I'm not the right person to solve this problem.

danm-de added 14 commits May 19, 2024 18:27
- Replaced XML doc comments with <inheritdoc> statement if applicable.
- Corrected unit tests (FromString).
- new behavior for decimal values in the JSON string (3.5 -> 35/10).
….cs (was a suggestion from @lipchev in a previous PR - which simplifies code reviews).
….cs (was a suggestion from @lipchev in a previous PR - which simplifies code reviews).
…al.cs (was a suggestion from @lipchev in a previous PR - which simplifies code reviews).
- replaced 'new CultureInfo("en-US")' with 'CultureInfo.GetCultureInfo("en-US")' which retrieves a cached, read-only instance of a culture.
@danm-de
Copy link
Owner

danm-de commented May 19, 2024

I made a few small changes:

  • updated XMLDOC comments (added inheritdoc where possible)
  • added section “Working with non-normalized fractions” to the README
  • added method overloads to avoid parameters with default values (preserve ABI compatibility)
  • fixed a few compiler warnings

I have also changed/added your suggestion about splitting the Fraction.ConvertFrom.cs file back. Code reviews of such large files are not very pleasant.

Readme.md Outdated Show resolved Hide resolved
@@ -41,6 +41,9 @@ There a three types of constructors available:
- `new Fraction (<numerator>, <denominator>)` using `BigInteger` for numerator and denominator.
- `new Fraction (<numerator>, <denominator>, <reduce>)` using `BigInteger` for numerator and denominator + `bool` to indicate if the resulting fraction shall be normalized (reduced).

> [!IMPORTANT]
> When creating improper fractions (by specifying the parameter `reduce: false`), please be sure to refer to the [Working with non-normalized fractions](#working-with-non-normalized-fractions) and the [Equality operators](#equality-operators) section for more information about the (side) effects.
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 have no particular preference, but have you considered actually making the Equality/HashCode based on reduced terms? Given that 123.00m is considered equal to 123m I don't think it would be inconsistent to say that 12300/100 is equal to 123/1 .

Copy link
Owner

Choose a reason for hiding this comment

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

I cannot estimate the performance impact on existing code. Currently, most users are likely to use normalized fractions anyway, then the following applies:

If State == FractionState.IsNormalized or _normalizationNotApplied == false is set for both fractions, then the numerator and denominator can simply be compared.

However, the situation is different with non-normalized fractions. The expensive method Reduce would have to be called every time for both GetHashCode and Equals. And we cannot cache the results (readonly struct).

Another question would be: What if for a use case the following applies: $\frac{1}{2} \neq \frac{2}{4}$ (because these are different representations in the user interface)?

Copy link
Owner

Choose a reason for hiding this comment

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

I actually had a long discussion with my colleagues back then. The majority believed that Equals should always be exact. Microsoft does not follow this rule. In our software we have a problem with this:
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the Equals is true then the HashCode should be equal as well, the opposite is not a given.

I cannot estimate the performance impact on existing code. Currently, most users are likely to use normalized fractions anyway, then the following applies:

The GetHashCode is mostly used when checking for stuff like collection.Contains(x) (where collection is actually hash-based)- otherwise we hit the Equals check, which for our scenario can be quite efficient. For actual GetHashCode- related checks, well as you said- it's more likely that somebody is doing the something custom, and can override the way the hash-code is calculated.
The unintentional / unexpected non-equality is what worries me a little- but we'll be fine :)

Copy link
Owner

Choose a reason for hiding this comment

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

I added the .Dump() output in no particular order, but of course: Equals(a,b) == true implied GetHashCode(a) == GetHashCode(b). In the example we have two different time zones - why are both values ​​the same? If I want to know if it is the same point in time, I would apply .ToUniversalTime() to both variables and then compare.

But hey, I'm open to everything. You said you wanted to use the data type in another project (https://github.com/angularsen/UnitsNet right?). What is the opinion there? Is my imagined “use case” perhaps nonsense?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, we've had lots of issues with Equality contract and how to make it so that "1 kg" == "1000 g". My personal feeling is that making it custom is way easier then making it work for everybody.. I asked the AI some time ago about a design pattern to justify an argument- it cited the principle of least surprise which I quite liked. So as soon as we open the door the optimized operations someone (new) might get surprised.

Copy link
Owner

Choose a reason for hiding this comment

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

POLS, I remember it. Geoffrey James has been quoted quite a bit.

Copy link
Owner

Choose a reason for hiding this comment

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

Let me sleep and think about it one night.

Copy link
Owner

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I don't know whoever thought it was necessary to have the Equals method differ from the == on this one 🤔

Readme.md Outdated Show resolved Hide resolved
@danm-de
Copy link
Owner

danm-de commented May 19, 2024

Do you have any other comments or suggestions? If not, I would merge it into the master branch.

@lipchev
Copy link
Contributor Author

lipchev commented May 19, 2024

I ran the ConsecutiveOperationsBenchmark without the extra reduction steps for the Multiply and (no big surprise) it looks like the chart with the Divide comparison:

ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1 10 145.7 ns 4.56 ns 0.25 ns 1.00 0.00 0.0019 32 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1 10 133.0 ns 8.66 ns 0.47 ns 0.91 0.00 0.0019 32 B 1.00
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1 10 477.9 ns 8.11 ns 0.44 ns 1.00 0.00 0.0048 32 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1 10 391.9 ns 8.30 ns 0.46 ns 0.82 0.00 0.0048 32 B 1.00
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1 20 294.6 ns 48.60 ns 2.66 ns 1.00 0.00 0.0019 32 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1 20 276.7 ns 55.50 ns 3.04 ns 0.94 0.01 0.0019 32 B 1.00
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1 20 957.5 ns 31.70 ns 1.74 ns 1.00 0.00 0.0048 32 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1 20 815.4 ns 23.32 ns 1.28 ns 0.85 0.00 0.0048 32 B 1.00
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1 100 1,789.3 ns 95.20 ns 5.22 ns 1.00 0.00 0.0591 992 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1 100 1,660.4 ns 103.54 ns 5.68 ns 0.93 0.00 0.0591 992 B 1.00
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1 100 5,053.9 ns 122.37 ns 6.71 ns 1.00 0.00 0.1297 859 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1 100 4,299.4 ns 60.84 ns 3.33 ns 0.85 0.00 0.1297 859 B 1.00
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10 10 408.8 ns 52.73 ns 2.89 ns 1.00 0.00 0.0019 32 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10 10 210.4 ns 34.27 ns 1.88 ns 0.51 0.00 0.0038 64 B 2.00
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10 10 1,217.2 ns 177.83 ns 9.75 ns 1.00 0.00 0.0038 32 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10 10 611.2 ns 34.33 ns 1.88 ns 0.50 0.00 0.0095 64 B 2.00
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10 20 1,151.7 ns 80.17 ns 4.39 ns 1.00 0.00 0.0248 416 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10 20 539.0 ns 131.31 ns 7.20 ns 0.47 0.01 0.0381 648 B 1.56
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10 20 2,834.0 ns 185.86 ns 10.19 ns 1.00 0.00 0.0687 433 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10 20 1,443.5 ns 26.32 ns 1.44 ns 0.51 0.00 0.1163 738 B 1.70
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10 100 20,747.8 ns 518.87 ns 28.44 ns 1.00 0.00 0.4883 8448 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10 100 3,565.0 ns 269.12 ns 14.75 ns 0.17 0.00 0.5112 8552 B 1.01
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10 100 35,193.9 ns 5,488.76 ns 300.86 ns 1.00 0.00 2.3193 14764 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10 100 9,426.9 ns 1,430.76 ns 78.42 ns 0.27 0.00 1.4191 8930 B 0.60
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 100 10 694.6 ns 63.37 ns 3.47 ns 1.00 0.00 0.0248 416 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 100 10 284.7 ns 23.75 ns 1.30 ns 0.41 0.00 0.0253 424 B 1.02
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 100 10 1,531.1 ns 51.19 ns 2.81 ns 1.00 0.00 0.0763 481 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 100 10 755.9 ns 71.81 ns 3.94 ns 0.49 0.00 0.0772 489 B 1.02
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 100 20 2,446.4 ns 48.65 ns 2.67 ns 1.00 0.00 0.0725 1224 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 100 20 623.1 ns 44.87 ns 2.46 ns 0.25 0.00 0.0706 1184 B 0.97
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 100 20 4,841.6 ns 124.93 ns 6.85 ns 1.00 0.00 0.3204 2030 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 100 20 1,694.6 ns 286.36 ns 15.70 ns 0.35 0.00 0.2079 1316 B 0.65
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 100 100 68,505.2 ns 2,077.71 ns 113.89 ns 1.00 0.00 0.7324 12304 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 100 100 4,170.2 ns 575.91 ns 31.57 ns 0.06 0.00 0.7858 13152 B 1.07
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 100 100 84,854.0 ns 12,233.63 ns 670.57 ns 1.00 0.00 3.6621 23437 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 100 100 11,178.5 ns 409.94 ns 22.47 ns 0.13 0.00 2.1973 13833 B 0.59
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1000 10 1,011.3 ns 44.47 ns 2.44 ns 1.00 0.00 0.0305 528 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1000 10 297.9 ns 30.35 ns 1.66 ns 0.29 0.00 0.0324 544 B 1.03
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1000 10 2,005.2 ns 59.80 ns 3.28 ns 1.00 0.00 0.1297 818 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1000 10 815.9 ns 7.71 ns 0.42 ns 0.41 0.00 0.0916 578 B 0.71
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1000 20 4,184.5 ns 19.11 ns 1.05 ns 1.00 0.00 0.0763 1368 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1000 20 655.2 ns 70.93 ns 3.89 ns 0.16 0.00 0.0877 1472 B 1.08
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1000 20 6,073.8 ns 157.03 ns 8.61 ns 1.00 0.00 0.4044 2576 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1000 20 1,821.9 ns 113.36 ns 6.21 ns 0.30 0.00 0.2499 1573 B 0.61
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1000 100 125,455.6 ns 2,579.47 ns 141.39 ns 1.00 0.00 0.7324 16056 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 1000 100 4,786.5 ns 881.84 ns 48.34 ns 0.04 0.00 1.0376 17448 B 1.09
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1000 100 163,936.8 ns 18,881.38 ns 1,034.95 ns 1.00 0.00 4.8828 30821 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 1000 100 12,485.2 ns 1,412.03 ns 77.40 ns 0.08 0.00 2.8534 18021 B 0.58
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10000 10 1,504.0 ns 6.62 ns 0.36 ns 1.00 0.00 0.0362 624 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10000 10 314.6 ns 21.33 ns 1.17 ns 0.21 0.00 0.0386 648 B 1.04
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10000 10 2,552.2 ns 51.12 ns 2.80 ns 1.00 0.00 0.1678 1075 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10000 10 864.5 ns 55.67 ns 3.05 ns 0.34 0.00 0.1097 690 B 0.64
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10000 20 5,320.7 ns 264.68 ns 14.51 ns 1.00 0.00 0.0916 1560 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10000 20 693.6 ns 338.49 ns 18.55 ns 0.13 0.00 0.1011 1704 B 1.09
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10000 20 7,370.7 ns 445.74 ns 24.43 ns 1.00 0.00 0.4654 2929 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10000 20 1,929.5 ns 327.09 ns 17.93 ns 0.26 0.00 0.2861 1805 B 0.62
                         
ReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10000 100 178,109.0 ns 1,447.72 ns 79.35 ns 1.00 0.00 0.9766 19320 B 1.00
NonReducedMultiplications ShortRun-.NET 8.0 .NET 8.0 10000 100 5,379.2 ns 2,477.77 ns 135.81 ns 0.03 0.00 1.2970 21704 B 1.12
                         
ReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10000 100 262,519.0 ns 4,459.44 ns 244.44 ns 1.00 0.00 5.8594 37337 B 1.00
NonReducedMultiplications ShortRun-.NET Framework 4.8 .NET Framework 4.8 10000 100 13,471.7 ns 871.72 ns 47.78 ns 0.05 0.00 3.5248 22225 B 0.60

So we are paying some performance over the decimal shifiting stuff... I hope it makes sense 😄

@lipchev
Copy link
Contributor Author

lipchev commented May 19, 2024

There is also a drop in the ExampleScenario:

Method Job Runtime Mean Error StdDev Ratio Gen0 Gen1 Allocated Alloc Ratio
PrepareSchedulesReduced MediumRun-.NET 8.0 .NET 8.0 9.441 μs 0.0464 μs 0.0680 μs 1.00 0.8698 0.0305 14.38 KB 1.00
PrepareSchedulesNonReduced MediumRun-.NET 8.0 .NET 8.0 8.032 μs 0.0428 μs 0.0586 μs 0.85 1.0071 0.0305 16.47 KB 1.15
                     
PrepareSchedulesReduced MediumRun-.NET Framework 4.8 .NET Framework 4.8 26.491 μs 0.0921 μs 0.1350 μs 1.00 4.7913 0.1221 29.52 KB 1.00
PrepareSchedulesNonReduced MediumRun-.NET Framework 4.8 .NET Framework 4.8 20.199 μs 0.0650 μs 0.0953 μs 0.76 5.1880 0.1526 31.88 KB 1.08

@danm-de
Copy link
Owner

danm-de commented May 19, 2024

So we are paying some performance over the decimal shifiting stuff... I hope it makes sense

Hmm, all I see from this is that execution speed depends largely on the numbers used and the consecutive operations selected. 🥲

@lipchev
Copy link
Contributor Author

lipchev commented May 19, 2024

I think It's like a staircase function - it's about "how many times over MaxInteger the result is"- once you pass it it's a plateau until the next increment (where dividing by 5/2 reduces nothing)

@danm-de danm-de merged commit 922677d into danm-de:master May 20, 2024
1 check passed
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.

None yet

2 participants